home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 23 / Amiga Format AFCD23 (Feb 1998, Issue 107).iso / +look_here_1st!+ / handy_tools / snoopdos / snoopdos_source / mainwin.c < prev    next >
C/C++ Source or Header  |  1997-02-27  |  148KB  |  5,220 lines

  1. /*
  2.  *        MAINWIN.C                                            vi:ts=4
  3.  *
  4.  *      Copyright (c) Eddy Carroll, September 1994.
  5.  *
  6.  *        This module handles the main SnoopDos window, outputting new events
  7.  *        to the window, menu options, etc.
  8.  */        
  9.  
  10. #include "system.h"
  11. #include "snoopdos.h"
  12. #include "gui.h"
  13.  
  14. #if 0
  15. #define DB(s)    KPrintF(s)
  16. #else
  17. #define DB(s)
  18. #endif
  19.  
  20. /*
  21.  *        In earlier versions, we used to lock layers whenever we rendered
  22.  *        event output to the SnoopDos main window buffer. This avoided
  23.  *        deadlocks with input.device. Now we use a different work around,
  24.  *        but for the sake of documentation, we leave the original calls
  25.  *        in (you never know when they might come in handy)
  26.  *
  27.  *        (The different workaround is to never monitor tasks that have
  28.  *        locked layers on the SnoopDos screen/window).
  29.  */
  30. #if 0
  31. #define LOCK_LAYERS    \
  32.     (LockLayerInfo(&MainWindow->WScreen->LayerInfo),    \
  33.      LockLayer(0, MainWindow->RPort->Layer))
  34.  
  35. #define UNLOCK_LAYERS \
  36.     (UnlockLayer(MainWindow->RPort->Layer),    \
  37.      UnlockLayerInfo(&MainWindow->WScreen->LayerInfo))
  38.  
  39. #else
  40. #define LOCK_LAYERS
  41. #define UNLOCK_LAYERS
  42. #endif
  43.  
  44.  
  45. /*
  46.  *        Interface to RawPutChar() in exec.library
  47.  *        (It takes a single parameter in D0)
  48.  */
  49. #pragma libcall SysBase RawPutChar 204 001
  50.  
  51. /*
  52.  *        These two are imported from SNOOPDOS.C
  53.  */
  54. extern char    Version[];
  55. extern char SnoopDosTitle[];
  56. extern char SnoopDosTitleKey[];
  57.  
  58. /*
  59.  *        Global vars used by the main window
  60.  */
  61. struct Gadget        *MainGadList;        /* Main gadget list            */
  62. struct Menu            *MainWinMenu;        /* Main window menu            */
  63. struct TextFont        *BufferFont;        /* Handle for buffer font    */
  64. struct TextAttr        *CurMainGadgetFA;    /* Current gadget font attr    */
  65. struct TextAttr        *CurMainBufferFA;    /* Current buffer font attr    */
  66.  
  67. struct RastPort        InvertRP;            /* Used when sizing columns    */
  68.  
  69. int NumBufLines;            /* Current number of lines in buffer    */
  70. int ChangedSpacing;            /* 1 = edited spacing since last redraw    */
  71. int ScrollDirection;        /* ID of current scroll gadget, or 0    */
  72. int ScrollCount;            /* Used for pacing INTUITICKS in scroll    */
  73. int RemovedGadgets;            /* True if gadgets removed from window    */
  74. int AwaitingResize;            /* True if waiting for window resize    */
  75.  
  76. #define RESIZE_DONE        0    /* Resize is now complete                */
  77. #define RESIZE_MIDDLE    1    /* Resize happened in middle of buffer    */
  78. #define RESIZE_BOTTOM    2    /* Resize happened at end of buffer        */
  79.  
  80. #define MOVECOL_FREE    0    /* Reposition column freely                */
  81. #define MOVECOL_FIXED    1    /* Reposition column fixed wrt RHS col    */
  82.  
  83. #define INVERT_HEADER    0    /* Invert only the header part of col    */
  84. #define INVERT_FULLBOX    1    /* Invert entire column                  */
  85.  
  86. int    BoxLeft;                /* Pixel offset of left edge of box        */
  87. int    BoxTop;                    /* Pixel offset of top edge of box        */
  88. int    BoxWidth;                /* Width of box in pixels                */
  89. int    BoxHeight;                /* Height of box in pixels                */
  90. int    BoxHeaderLine;            /* Baseline for box header text            */
  91. int    BoxInLeft;                /* Pixel offset of inside top of box    */
  92. int    BoxInTop;                /* Pixel offset of inside left of box    */
  93. int    BoxInWidth;                /* Inside width of box                    */
  94. int    BoxInHeight;            /* Inside height of box                    */
  95. int    BoxCharWidth;            /* Width of a single character in box    */
  96. int BoxCharHeight;            /* Height of a single character in box    */
  97. int    BoxCols;                /* Number of columns of text in box        */
  98. int    BoxRows;                /* Number of rows of text in box        */
  99. int    BoxSpacing;                /* Height between font baselines in box    */
  100. int    BoxLowest;                /* Bottom point of box scroll arrow        */
  101. int    BoxBaseline;            /* Baseline of text line 0 in box        */
  102. int    VBorderGap;                /* Aspect-adjusted space betw hrz lines    */
  103. int    LogButtonLeft;            /* Left (X) Position of Logfile button    */
  104. int LogButtonID;            /* Gadget ID of log button on view        */
  105.  
  106. int    LeftCol;                /* Leftmost displayed column            */
  107. int    RightCol;                /* Rightmost displayed column            */
  108.  
  109. int DraggingRow;            /* 1 if dragging a row with mouse        */
  110. int SelectRow;                /* Row to draw highlighted                */
  111. int DraggingColumn;            /* 1 if dragging a column with mouse    */
  112. int ClickStartCol;            /* Col pos where mouse first clicked    */
  113. int ClickCurCol;            /* Current column where mouse clicked    */
  114. int SelectStartCol;            /* Start position of selected column    */
  115. int DragOrigColWidth;        /* Starting width of widening column    */
  116. int NextOrigColWidth;        /* Starting width of following column    */
  117. int OrigLeftCol;            /* Starting position of left column        */
  118. int OrigBufWidth;            /* Starting width of buffer                */
  119. int MovedColumn;            /* True if column position was altered    */
  120.  
  121. EventFormat *DragEvent;        /* Points to column being widened        */
  122. EventFormat *SelectEvent;    /* Points to column being highlighted    */
  123.  
  124. BPTR   LogFile;                /* Handle of output log file            */
  125. UBYTE *LogBuffer;            /* Buffer for buffered i/o (or NULL)    */
  126. ULONG  LogBufferSize;        /* Size of logfile output buffer        */
  127. ULONG  LogBufferPos;        /* Current position in output buffer    */
  128.  
  129. UBYTE  MainKeyboard[KB_SHORTCUT_SIZE];    /* For keyboard equivs        */
  130.  
  131. /*
  132.  *        This table is used to select the appropriate row qualifier
  133.  *        for the row selection mechanism
  134.  */
  135. UWORD RowQualTable[] = {
  136.     0,                /* ROWQUAL_ANY     -- ignore qualifiers                */
  137.     0,                /* ROWQUAL_NONE     -- don't allow any qualifiers        */
  138.     IE_SHIFT,        /* ROWQUAL_SHIFT -- only select if SHIFT is pressed    */
  139.     IE_ALT,            /* ROWQUAL_ALT   -- only select if ALT is pressed    */
  140.     IE_CTRL,        /* ROWQUAL_CTRL     -- only select if CTRL is pressed    */
  141.     IE_ALL            /* ROWQUAL_ALL   -- only select if any qual pressed    */
  142. };
  143.  
  144. /*
  145.  *        Now our font structures.
  146.  *
  147.  *        We do everything in terms of FontAttr structures, and whenever a
  148.  *        window needs a font, it OpenFont's it itself. This has several
  149.  *        advantages: it allows each window to dynamically select the
  150.  *        best-fitting font, and it also ensures no problems if we have a
  151.  *        window open with one font and the user than changes the font
  152.  *        to something else -- the first window will still have a valid
  153.  *        lock on the old font.
  154.  */
  155. char   BufferLine[MAX_BUFFER_WIDTH];  /* Storage area for buffer lines */
  156.  
  157. struct TextAttr TopazFontAttr  = {"topaz.font",      8, FS_NORMAL, FPB_ROMFONT };
  158. struct TextAttr SystemFontAttr = {SystemFontName, 0, FS_NORMAL, FPB_DISKFONT};
  159. struct TextAttr WindowFontAttr = {WindowFontName, 0, FS_NORMAL, FPB_DISKFONT};
  160. struct TextAttr BufferFontAttr = {BufferFontName, 0, FS_NORMAL, FPB_DISKFONT};
  161.  
  162. struct {
  163.     struct TextAttr *gadgetfa;
  164.     struct TextAttr *bufferfa;
  165. } MainWindowFontList[] = {
  166.     &WindowFontAttr,    &BufferFontAttr,
  167.     &SystemFontAttr,    &BufferFontAttr,
  168.     &WindowFontAttr,    &SystemFontAttr,
  169.     &SystemFontAttr,    &SystemFontAttr,
  170.     &TopazFontAttr,        &SystemFontAttr,
  171.     &TopazFontAttr,        &TopazFontAttr,
  172.     NULL,                NULL
  173. };
  174.  
  175. /*
  176.  *        This is used by the boopsi proportional gadgets
  177.  */
  178. static struct TagItem RZ_MapTags[] = {
  179.     PGA_Top, ICSPECIAL_CODE,
  180.     TAG_END
  181. };
  182.  
  183. /*
  184.  *        This holds the image pointers for our BOOPSI scroll gadgets
  185.  */
  186. struct Image        *ScrollImage[4];
  187.  
  188. /*
  189.  *        This structure is used to define our four scroll gadgets. We
  190.  *        define the relative positions of each gadget in terms of the
  191.  *        formula (Scale * Width + offset) or (Scale * Height + offset)
  192.  *        where Width and Height are taken from the image structure.
  193.  */
  194. struct ScrollData {
  195.     int        gadgetid;            /* ID of the gadget                        */
  196.     int        imagetype;            /* Which type of image we're creating    */
  197.     LONG    widthscale;            /* Scale applied to width                */
  198.     LONG    widthoffset;        /* Offset applied to width                 */
  199.     LONG    heightscale;        /* Scale applied to height                */
  200.     LONG    heightoffset;        /* Offset applied to offset                */
  201.     LONG    borderpos;            /* Tag for border containing gadget        */
  202. } ScrollArrows[] = {
  203.     GID_LEFTARROW,    LEFTIMAGE,    -2, -17,    -1, 1,        GA_BottomBorder,
  204.     GID_RIGHTARROW,    RIGHTIMAGE,    -1, -17,    -1, 1,        GA_BottomBorder,
  205.     GID_UPARROW,    UPIMAGE,    -1, 1,        -2, -9,        GA_RightBorder,
  206.     GID_DOWNARROW,    DOWNIMAGE,    -1, 1,        -1, -9,        GA_RightBorder
  207. };
  208.     
  209. /*
  210.  *        Now the gadgets for the main window
  211.  */
  212. struct MainGadgets {
  213.     UWORD    gadgid;            /* Gadget ID                    */
  214.     UWORD    stringid;        /* ID of label name             */
  215.     UBYTE    col4;            /* Column in 4 x 2 layout        */
  216.     UBYTE    row4;            /* Row in 4 x 2 layout            */
  217.     UBYTE    col6;            /* Column in 4 x 2 layout        */
  218.     UBYTE    row6;            /* Row in 4 x 2 layout            */
  219.     UBYTE    col8;            /* Column in 4 x 2 layout        */
  220.     UBYTE    col12;            /* Column in 12 x 1 layout        */
  221.     UBYTE    widthtype;        /* 0=narrow, 1=wide, 2=status     */
  222. };
  223.  
  224.  
  225. struct MainGadgets MainGadgs[] = {
  226.  GID_HIDE,        MSG_HIDE_GAD,          0, 2,     0,    1,    0,    4,    MAIN_NARROW,
  227.  GID_QUIT,        MSG_QUIT_GAD,          1, 2,     1,    1,    1,    5,    MAIN_NARROW,
  228.  GID_PAUSE,        MSG_PAUSE_GAD,          0, 1,     2,    1,    2,    6,    MAIN_NARROW_TOGGLE,
  229.  GID_DISABLE,    MSG_DISABLE_GAD,      1, 1,     3,    1,    3,    7,    MAIN_NARROW_TOGGLE,
  230.  GID_OPENLOG,    MSG_OPENLOG_GAD,    2, 1,     4, 0,    4,    8,  MAIN_WIDE,
  231.  GID_APPENDLOG,    MSG_APPENDLOG_GAD,    2, 1,     4,    0,    4,    8,    MAIN_WIDE_INVIS,
  232.  GID_STARTLOG,    MSG_STARTLOG_GAD,    2, 1,     4,    0,    4,    8,    MAIN_WIDE_INVIS,
  233.  GID_SERIALLOG,    MSG_SERIALLOG_GAD,    2, 1,     4, 0,  4,  8,    MAIN_WIDE_INVIS,
  234.  GID_CLOSELOG,    MSG_CLOSELOG_GAD,    2, 1,     4, 0,    4,    8,  MAIN_WIDE_INVIS,
  235.  GID_SETUP,        MSG_SETUP_GAD,          3, 1,     5,    0,    6,    10,    MAIN_WIDE,
  236.  GID_SAVESET,    MSG_SAVESET_GAD,      2, 2,     4,    1,    5,    9,    MAIN_WIDE,
  237.  GID_FUNCTION,    MSG_FUNCTION_GAD,    3, 2,     5,    1,    7,    11,    MAIN_WIDE,
  238.  GID_STATUS,    MSG_STATUS_GAD,          0, 0,     0,    0,    0,    0,    MAIN_STATUS,
  239.   0, 0, 0, 0, 0, 0, 0, 0, 0
  240. };
  241.  
  242.  
  243. /*
  244.  *        Names of gadgets in main window for calculating minimum width
  245.  */
  246. int MainWidthLeftText[] = {
  247.     MSG_PAUSE_GAD,
  248.     MSG_DISABLE_GAD,
  249.     MSG_HIDE_GAD,
  250.     MSG_QUIT_GAD,
  251.     0
  252. };
  253.  
  254. int MainWidthRightText[] = {
  255.     MSG_OPENLOG_GAD,
  256.     MSG_APPENDLOG_GAD,
  257.     MSG_STARTLOG_GAD,
  258.     MSG_SERIALLOG_GAD,
  259.     MSG_CLOSELOG_GAD,
  260.     MSG_SAVESET_GAD,
  261.     MSG_FUNCTION_GAD,
  262.     MSG_SETUP_GAD,
  263.     0
  264. };
  265.  
  266.  
  267. /*
  268.  *        SnoopDos Menus
  269.  *
  270.  *        -- Warning -- 
  271.  *
  272.  *        If you re-arrange the order of menu items or sub-items, you must
  273.  *        make sure to update the MENU_xxxx #define's at the end to reflect
  274.  *        the new ordering!
  275.  *
  276.  *        (Remember to count bars when working out item numbers, and that
  277.  *        menu/item numbers start from zero, not one.)
  278.  */
  279.  
  280. #define DIS            NM_ITEMDISABLED
  281. #define TICK        (CHECKIT | MENUTOGGLE)
  282. #define TICKED        (CHECKIT | MENUTOGGLE | CHECKED)
  283. #define BAR            NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL
  284. #define MENU_END    NM_END,  NULL, NULL, 0,    0, NULL
  285.  
  286. #define M(type,title_id,flags,mid) \
  287.     type,    (UBYTE *)(title_id), 0, flags, 0, (APTR)(mid)
  288.  
  289. #define MX(type,title_id,flags,ex,mid) \
  290.     type,    (UBYTE *)(title_id), 0, flags, ex,(APTR)(mid)
  291.  
  292. struct NewMenu MainMenu[] = {
  293.  
  294.     /*
  295.      *        Project menu
  296.      */
  297.     M(    NM_TITLE,    MSG_PROJECT_MENU,            0,        0),
  298.     M(   NM_ITEM,   MSG_PROJECT_OPENLOG,        0,        MID_OPENLOG),
  299.     M(     NM_ITEM,    MSG_PROJECT_CLOSELOG,        DIS,    MID_CLOSELOG),
  300.          BAR,
  301.     MX(   NM_ITEM,    MSG_PROJECT_PAUSE,            TICK,   ~8,        MID_PAUSE),
  302.     MX(   NM_ITEM,    MSG_PROJECT_DISABLE,        TICK,   ~16,    MID_DISABLE),
  303.          BAR,
  304.     M(    NM_ITEM,  MSG_PROJECT_STEP,           0,      MID_STEP),
  305.          BAR,
  306.     M(    NM_ITEM,  MSG_PROJECT_PRI,            0,      0),
  307.     MX(    NM_SUB,    MSG_PROJECT_PRI_A,            TICK,   ~1,        MID_CHANGEPRI),
  308.     MX(    NM_SUB,    MSG_PROJECT_PRI_B,            TICK,   ~2,        MID_CHANGEPRI),
  309.     MX(    NM_SUB,    MSG_PROJECT_PRI_C,            TICK,   ~4,        MID_CHANGEPRI),
  310.     MX(    NM_SUB,    MSG_PROJECT_PRI_D,            TICK,   ~8,        MID_CHANGEPRI),
  311.     MX(    NM_SUB,    MSG_PROJECT_PRI_E,            TICK,   ~16,    MID_CHANGEPRI),
  312.     MX(    NM_SUB,    MSG_PROJECT_PRI_F,            TICK,   ~32,    MID_CHANGEPRI),
  313.     MX(    NM_SUB,    MSG_PROJECT_PRI_G,            TICK,   ~64,    MID_CHANGEPRI),
  314.          BAR,
  315.     M(     NM_ITEM,    MSG_PROJECT_HELP,            0,        MID_HELP),
  316.     M(     NM_ITEM,    MSG_PROJECT_ABOUT,            0,        MID_ABOUT),
  317.          BAR,
  318.     M(     NM_ITEM,    MSG_PROJECT_HIDE,            0,        MID_HIDE),
  319.          BAR,
  320.     M(     NM_ITEM,    MSG_PROJECT_QUIT,            0,        MID_QUIT),
  321.  
  322.     /*
  323.      *        Windows menu
  324.      */
  325.     M(  NM_TITLE,    MSG_WINDOWS_MENU,            0,        0),
  326.     M(   NM_ITEM,    MSG_WINDOWS_SETUP,            0,        MID_SETUP),
  327.     M(   NM_ITEM,    MSG_WINDOWS_FUNCTION,        0,        MID_FUNCTION),
  328.     M(   NM_ITEM,    MSG_WINDOWS_FORMAT,            0,        MID_FORMAT),
  329.          BAR,
  330.     M(   NM_ITEM,   MSG_WINDOWS_WIDTH,            0,      0),
  331.     M(      NM_SUB,    MSG_WINDOWS_WIDTH_1,        0,         MID_SETWIDTH),
  332.     M(      NM_SUB,    MSG_WINDOWS_WIDTH_2,        0,         MID_SETWIDTH),
  333.     M(      NM_SUB,    MSG_WINDOWS_WIDTH_3,        0,         MID_SETWIDTH),
  334.     M(      NM_SUB,    MSG_WINDOWS_WIDTH_4,        0,         MID_SETWIDTH),
  335.     M(      NM_SUB,    MSG_WINDOWS_WIDTH_5,        0,         MID_SETWIDTH),
  336.          BAR,
  337.     M(     NM_ITEM,    MSG_WINDOWS_SPACING,        0,        0),
  338.     MX(      NM_SUB,    MSG_WINDOWS_SPACING_NONE,    TICKED, ~1,    MID_SPACE_NONE),
  339.     MX(      NM_SUB,    MSG_WINDOWS_SPACING_1P,        TICK,   ~2,    MID_SPACE_1P),
  340.     MX(      NM_SUB,    MSG_WINDOWS_SPACING_2P,        TICK,   ~4,    MID_SPACE_2P),
  341.  
  342.     M(     NM_ITEM,    MSG_WINDOWS_REFRESH,        0,        0),
  343.     MX(   NM_SUB,    MSG_WINDOWS_REF_SIMPLE,        TICK,   ~1, MID_REF_SIMPLE),
  344.     MX(   NM_SUB,    MSG_WINDOWS_REF_SMART,        TICKED, ~2, MID_REF_SMART),
  345.  
  346.     M(   NM_ITEM,    MSG_WINDOWS_ALIGN,            0,        0),
  347.     MX(      NM_SUB,    MSG_WINDOWS_ALIGN_LEFT,        TICKED, ~1, MID_ALIGN_LEFT),
  348.     MX(      NM_SUB,    MSG_WINDOWS_ALIGN_RIGHT,    TICK,   ~2, MID_ALIGN_RIGHT),
  349.  
  350.     M(   NM_ITEM,   MSG_WINDOWS_ROW_QUAL,        0,      0),
  351.     MX(      NM_SUB,    MSG_WINDOWS_ROW_ANY,        TICKED,    ~1,  MID_ROWQUAL),
  352.     MX(      NM_SUB,    MSG_WINDOWS_ROW_NONE,        TICK,     ~2,  MID_ROWQUAL),
  353.     MX(      NM_SUB,    MSG_WINDOWS_ROW_SHIFT,        TICK,     ~4,  MID_ROWQUAL),
  354.     MX(      NM_SUB,    MSG_WINDOWS_ROW_ALT,        TICK,     ~8,  MID_ROWQUAL),
  355.     MX(      NM_SUB,    MSG_WINDOWS_ROW_CTRL,        TICK,     ~16, MID_ROWQUAL),
  356.     MX(      NM_SUB,    MSG_WINDOWS_ROW_ALL,        TICK,     ~32, MID_ROWQUAL),
  357.  
  358.          BAR,
  359.     M(     NM_ITEM,    MSG_WINDOWS_SHOWSTATUS,        TICKED, MID_STATUS),
  360.     M(   NM_ITEM,   MSG_WINDOWS_SHOWGADGETS,    TICKED, MID_GADGETS),
  361.          BAR,
  362.     M(     NM_ITEM,    MSG_WINDOWS_AUTO_OPEN,        TICK,    MID_AUTO_OPEN),
  363.     M(   NM_ITEM,   MSG_WINDOWS_DISABLE_HIDDEN, TICK,   MID_DISABLE_HIDDEN),
  364.  
  365.     /*
  366.      *        Settings menu
  367.      */
  368.     M(    NM_TITLE,    MSG_SETTINGS_MENU,            0,        0),
  369.     M(     NM_ITEM,    MSG_SETTINGS_LOAD,            0,        MID_LOAD),
  370.     M(     NM_ITEM,    MSG_SETTINGS_SAVE,            0,        MID_SAVE),
  371.     M(     NM_ITEM,    MSG_SETTINGS_SAVEAS,        0,        MID_SAVEAS),
  372.          BAR,
  373.     M(     NM_ITEM,    MSG_SETTINGS_RESETDEFAULTS,    0,        MID_RESET),
  374.     M(     NM_ITEM,    MSG_SETTINGS_LASTSAVED,        0,        MID_LASTSAVED),
  375.     M(     NM_ITEM,    MSG_SETTINGS_RESTORE,        0,        MID_RESTORE),
  376.          BAR,
  377.     M(     NM_ITEM,    MSG_SETTINGS_CREATEICONS,    TICK,   MID_ICONS),
  378.  
  379.     /*
  380.      *        Buffer menu
  381.      */
  382.     M(    NM_TITLE,    MSG_BUFFER_MENU,            0,        0),
  383.     M(     NM_ITEM,    MSG_BUFFER_COPYWIN,            0,        MID_COPYWIN),
  384.     M(     NM_ITEM,    MSG_BUFFER_COPYBUF,            0,        MID_COPYBUF),
  385.          BAR,
  386.     M(   NM_ITEM,   MSG_BUFFER_SAVEWIN,            0,        MID_SAVEWIN),
  387.     M(     NM_ITEM,    MSG_BUFFER_SAVEBUF,            0,        MID_SAVEBUF),
  388.          BAR,
  389.     M(     NM_ITEM,    MSG_BUFFER_CLEARBUF,        0,        MID_CLEARBUF),
  390.  
  391.     MENU_END
  392. };
  393.  
  394. /*
  395.  *        These defines are used when adjusting the menu settings on the fly
  396.  */
  397.  
  398. #define MENU_OPENLOG        FULLMENUNUM(0,  0, NOSUB)
  399. #define MENU_CLOSELOG        FULLMENUNUM(0,  1, NOSUB)
  400. #define MENU_PAUSE            FULLMENUNUM(0,  3, NOSUB)
  401. #define MENU_DISABLE        FULLMENUNUM(0,  4, NOSUB)
  402. #define MENU_CHANGEPRI        FULLMENUNUM(0,  8, 0)
  403. #define MENU_NUMPRI            7                /* Number of menu priorities */
  404. #define MENU_HIDE            FULLMENUNUM(0,  13, NOSUB)
  405.  
  406. #define MENU_SPACING0        FULLMENUNUM(1,  6, 0)
  407. #define MENU_SPACING1        FULLMENUNUM(1,  6, 1)
  408. #define MENU_SPACING2        FULLMENUNUM(1,  6, 2)
  409. #define MENU_SIMPLE            FULLMENUNUM(1,  7, 0)
  410. #define MENU_SMART            FULLMENUNUM(1,  7, 1)
  411. #define MENU_AL_LEFT        FULLMENUNUM(1,  8, 0)
  412. #define MENU_AL_RIGHT        FULLMENUNUM(1,  8, 1)
  413. #define MENU_ROWQUAL_ANY    FULLMENUNUM(1,  9, 0)
  414. #define MENU_ROWQUAL_NONE    FULLMENUNUM(1,  9, 1)
  415. #define MENU_ROWQUAL_SHIFT    FULLMENUNUM(1,  9, 2)
  416. #define MENU_ROWQUAL_ALT    FULLMENUNUM(1,  9, 3)
  417. #define MENU_ROWQUAL_CTRL    FULLMENUNUM(1,  9, 4)
  418. #define MENU_ROWQUAL_ALL    FULLMENUNUM(1,  9, 5)
  419. #define MENU_STATUS            FULLMENUNUM(1, 11, NOSUB)
  420. #define MENU_GADGETS        FULLMENUNUM(1, 12, NOSUB)
  421. #define MENU_AUTO_OPEN        FULLMENUNUM(1, 14, NOSUB)
  422. #define MENU_DIS_HIDDEN     FULLMENUNUM(1, 15, NOSUB)
  423.  
  424. #define MENU_ICONS            FULLMENUNUM(2,  8, NOSUB)
  425.  
  426. /*****************************************************************************
  427.  *
  428.  *        Start of functions!
  429.  *
  430.  *****************************************************************************/
  431.  
  432. /*
  433.  *        GetTimeStr(datestamp)
  434.  *
  435.  *        Returns a pointer to a string giving the current time in
  436.  *        hours, mins, and seconds. The string remains valid until
  437.  *        the next time this function is called.
  438.  *
  439.  *        If the datestamp parameter is NULL, then the current date is
  440.  *        used instead.
  441.  */
  442. char *GetTimeStr(struct DateStamp *ds)
  443. {
  444.     static char timestr[20];
  445.     struct DateStamp ourds;
  446.     struct DateTime datetime;
  447.  
  448.     if (!ds) {
  449.         ds = &ourds;
  450.         DateStamp(ds);
  451.     }
  452.     datetime.dat_Stamp     = *ds;
  453.     datetime.dat_Format     = FORMAT_DOS;
  454.     datetime.dat_Flags     = 0;
  455.     datetime.dat_StrDay     = NULL;
  456.     datetime.dat_StrDate = NULL;
  457.     datetime.dat_StrTime = timestr;
  458.     DateToStr(&datetime);
  459.     return (timestr);
  460. }
  461.  
  462. /*
  463.  *        UpdateStatus(void)
  464.  *
  465.  *        Determines what the current status is (by examining various
  466.  *        variables) and sets the status line to display an appropriate
  467.  *        message. Call whenever the status may have changed.
  468.  *
  469.  *        Things which can cause a status change: pause/unpause, disable/enable,
  470.  *        open log/close log
  471.  */
  472. void UpdateStatus(void)
  473. {
  474.     if (Paused || Disabled) {
  475.         char *timestr;
  476.         char *merid;
  477.         char *msg;
  478.         int hour;
  479.         int min;
  480.  
  481.         if (Paused) {
  482.             timestr = GetTimeStr(&PauseDateStamp);
  483.             msg        = MSG(MSG_STATUS_PAUSED);
  484.         } else {
  485.             timestr = GetTimeStr(&DisableDateStamp);
  486.             msg        = MSG(MSG_STATUS_DISABLED);
  487.         }
  488.         hour     = atoi(timestr);
  489.         min      = atoi(timestr+3);
  490.         merid     = "AM";
  491.         if (hour >= 12) {
  492.             hour -= 12;
  493.             merid = "PM";
  494.         }
  495.         mysprintf(StatusLineText, msg, (hour ? hour : 12), min, merid);
  496.     } else if (LogActive) {
  497.         int msgid;
  498.  
  499.         switch (CurrentLogType) {
  500.             case LT_FILE:            msgid = MSG_STATUS_LOGFILE;        break;
  501.             case LT_DEVICE:            msgid = MSG_STATUS_LOGDEVICE;    break;
  502.             case LT_DEBUG:            msgid = MSG_STATUS_LOGDEBUG;    break;
  503.         }
  504.         mysprintf(StatusLineText, MSG(msgid), CurrentLogName);
  505.     } else
  506.         strcpy(StatusLineText, MSG(MSG_STATUS_NORMAL));
  507.     
  508.     if (MainWindow) {
  509.         GT_SetGadgetAttrs(Gadget[GID_STATUS], MainWindow, NULL,
  510.                           GTTX_Text, StatusLineText,
  511.                           TAG_DONE);
  512.     }
  513. }
  514.  
  515. /*
  516.  *        SetLogGadget(logmode, refresh)
  517.  *
  518.  *        Displays the appropriate gadget in the main window according to the
  519.  *        type of logging method chosen. If the logfile is currently open,
  520.  *        the "close log" gadget is displayed, otherwise the correct "open"
  521.  *        gadget is displayed according to the logmode: LOGMODE_PROMPT,
  522.  *        LOGMODE_APPEND, LOGMODE_OVERWRITE, or LOGMODE_SERIALPORT
  523.  *
  524.  *        If the refresh parameter is LG_REFRESH, then refresh gadgets.
  525.  *        LG_NOREFRESH means don't refresh gadgets (i.e. assume we're being
  526.  *        called from the gadget creation routine).
  527.  */
  528. void SetLogGadget(int logmode, int refresh)
  529. {
  530.     static int loggadgets[] = {
  531.         GID_OPENLOG, GID_APPENDLOG, GID_STARTLOG, GID_SERIALLOG,
  532.         GID_CLOSELOG, 0
  533.     };
  534.     int gadid;
  535.     int i;
  536.  
  537.     if (refresh == LG_REFRESH && !MainWindow)
  538.         return;
  539.  
  540.     switch (logmode) {
  541.         case LOGMODE_PROMPT:        gadid = GID_OPENLOG;        break;
  542.         case LOGMODE_APPEND:        gadid = GID_APPENDLOG;        break;
  543.         case LOGMODE_SERIALPORT:    gadid = GID_SERIALLOG;        break;
  544.         case LOGMODE_OVERWRITE:        gadid = GID_STARTLOG;        break;
  545.     }
  546.     if (LogActive)
  547.         gadid = GID_CLOSELOG;
  548.  
  549.     for (i = 0; loggadgets[i]; i++) {
  550.         struct Gadget *gad = Gadget[loggadgets[i]];
  551.         int gadpos;
  552.         
  553.         if (refresh == LG_REFRESH)
  554.              gadpos = RemoveGadget(MainWindow, gad);
  555.  
  556.         if (loggadgets[i] == gadid)
  557.             gad->LeftEdge = LogButtonLeft;
  558.         else
  559.             gad->LeftEdge = INVIS_LEFT_EDGE;
  560.  
  561.         if (refresh == LG_REFRESH) {
  562.             AddGadget(MainWindow, gad, gadpos);
  563.             RefreshGList(gad, MainWindow, NULL, 1);
  564.         }
  565.     }
  566.     LogButtonID = gadid;
  567. }
  568.  
  569. /*
  570.  *        WriteLog(string)
  571.  *
  572.  *        Writes the specified string to the logfile, handling buffering
  573.  *        etc. as required.
  574.  *
  575.  *        If string is NULL, then flushes any buffers as necessary.
  576.  */
  577. void WriteLog(char *string)
  578. {
  579.     int len;
  580.  
  581.     if (!LogActive)
  582.         return;
  583.     
  584.     if (string == NULL) {
  585.         /*
  586.          *        Flush output
  587.          */
  588.         if (LogFile && LogBuffer && LogBufferPos) {
  589.             Write(LogFile, LogBuffer, LogBufferPos);
  590.             LogBufferPos = 0;
  591.         }
  592.         return;
  593.     }
  594.  
  595.     if (CurrentLogType == LT_DEBUG) {
  596.         /*
  597.          *        Output string to serial terminal, expanding LF's to CR/LF's
  598.          *        as we go.
  599.          */
  600.         while (*string) {
  601.             if (*string == '\n')
  602.                 RawPutChar('\r');
  603.             RawPutChar(*string++);
  604.         }
  605.         return;
  606.     }
  607.     len = strlen(string);
  608.     if (LogBuffer) {
  609.         /*
  610.          *        Buffered i/o so add string to buffer; if necessary, flush
  611.          *        buffer first.
  612.          */
  613.         if ((LogBufferPos + len) > LogBufferSize) {
  614.             Write(LogFile, LogBuffer, LogBufferPos);
  615.             LogBufferPos = 0;
  616.         }
  617.         memcpy(LogBuffer + LogBufferPos, string, len);
  618.         LogBufferPos += len;
  619.     } else {
  620.         /*
  621.          *        Plain unbuffered i/o
  622.          */
  623.         Write(LogFile, string, strlen(string));
  624.     }
  625. }
  626.  
  627. /*
  628.  *        CloseLog()
  629.  *
  630.  *        Closes the currently open logfile (if any)
  631.  */
  632. void CloseLog(void)
  633. {
  634.     char msg[100];
  635.  
  636.     if (!LogActive)
  637.         return;
  638.     
  639.     mysprintf(msg, MSG(MSG_LOG_STOP), GetTimeStr(NULL));
  640.     WriteLog(msg);
  641.     WriteLog(NULL);    /* Flush output */
  642.     LogActive = 0;
  643.     SetLogGadget(DefaultLogMode, LG_REFRESH);
  644.     UpdateStatus();
  645.     SetMenuOptions();
  646.     if (LogFile) {
  647.         int oldpaused = Paused;
  648.  
  649.         Paused = 0;
  650.         Close(LogFile);
  651.         LogFile = NULL;
  652.         Paused = oldpaused;
  653.     }
  654.     if (LogBuffer) {
  655.         FreeMem(LogBuffer, LogBufferSize);
  656.         LogBuffer = NULL;
  657.     }
  658. }
  659.  
  660. /*
  661.  *        WriteLogHeader()
  662.  *
  663.  *        Writes the standard SnoopDos header line to the logfile using the
  664.  *        current logfile format
  665.  */
  666. void WriteLogHeader(void)
  667. {
  668.     FormatEvent(LogEFormat, NULL, BufferLine+1, 0, LogWidth - 1);
  669.     BufferLine[0]           = ' ';
  670.     BufferLine[LogWidth+1] = '\n';
  671.     BufferLine[LogWidth+2] = 0;
  672.     WriteLog(BufferLine);
  673.     UnderlineTitles(LogEFormat, BufferLine+1, '-');
  674.     BufferLine[LogWidth+1] = '\n';
  675.     WriteLog(BufferLine);
  676. }
  677.  
  678. /*
  679.  *        OpenLog(mode, filename)
  680.  *
  681.  *        Opens the named file for logging. If a log file is already open,
  682.  *        then closes the existing file first.
  683.  *
  684.  *        Mode is set to:
  685.  *
  686.  *            LOGMODE_PROMPT       - Prompt user if file exits for append/overwrite
  687.  *            LOGMODE_APPEND       - Automatically append to file if it exists
  688.  *            LOGMODE_OVERWRITE  - Automatically overwrite file it if exitsts
  689.  *            LOGMODE_SERIALPORT - Direct output to debugger on serial port
  690.  *
  691.  *        Filename is ignored if mode is set to LOGMODE_SERIALPORT.
  692.  *
  693.  *        Returns 1 for success, 0 for failure (couldn't open file?)
  694.  */
  695. int OpenLog(int logmode, char *filename)
  696. {
  697.     APTR oldwinptr = *TaskWindowPtr;
  698.     char startmsg[100];
  699.     char timestr[20];
  700.     char datestr[20];
  701.     char weekday[20];
  702.     struct DateTime datetime;
  703.     int pausestate = Paused;
  704.  
  705.     Paused = 0;
  706.     if (LogActive)
  707.         CloseLog();
  708.     
  709.     if (logmode == LOGMODE_SERIALPORT) {
  710.         CurrentLogType = LT_DEBUG;
  711.     } else {
  712.         int filemode;
  713.         int buffer = 0;
  714.  
  715.         strcpy(CurrentLogName, filename);
  716.         *TaskWindowPtr = (APTR)-1;
  717.         if (IsFileSystem(filename))
  718.             CurrentLogType = LT_FILE;
  719.         else
  720.             CurrentLogType = LT_DEVICE;
  721.         *TaskWindowPtr = oldwinptr;
  722.  
  723.         if (logmode == LOGMODE_PROMPT && CurrentLogType == LT_FILE) {
  724.             BPTR lock = Lock(filename, ACCESS_READ);
  725.  
  726.             if (lock) {
  727.                 UnLock(lock);
  728.                 switch (GetResponse(MSG(MSG_REQ_APPEND_OVERWRITE_CANCEL),
  729.                                     MSG(MSG_REQ_FILE_EXISTS), filename)) {
  730.  
  731.                     case 1:        filemode = MODE_READWRITE;        break;
  732.                     case 2:        filemode = MODE_NEWFILE;        break;
  733.                     default:    goto open_okay;
  734.                 }
  735.             } else
  736.                 filemode = MODE_NEWFILE;
  737.         } else if (logmode == LOGMODE_APPEND)
  738.             filemode = MODE_READWRITE;
  739.         else
  740.             filemode = MODE_NEWFILE;
  741.  
  742.         LogFile = Open(filename, filemode);
  743.         if (!LogFile)
  744.             goto open_failed;
  745.  
  746.         if (filemode == MODE_READWRITE) {
  747.             Seek(LogFile, 0, OFFSET_END);
  748.             Write(LogFile, "\n", 1); /* Leave space between multiple logs */
  749.         }
  750.  
  751.         /*
  752.          *        Now select buffer type for file according to gadget and
  753.          *        file type.
  754.          */
  755.         switch (CurSettings.Setup.FileIOType) {
  756.             case FILE_AUTOMATIC:
  757.                 if (CurrentLogType == LT_FILE)
  758.                     buffer = 1;
  759.                 break;
  760.  
  761.             case FILE_IMMEDIATE:        buffer = 0;    break;
  762.             case FILE_BUFFERED:            buffer = 1; break;
  763.         }
  764.         if (buffer) {
  765.             /*
  766.              *        Allocate a buffer for file i/o. Doesn't matter if
  767.              *        it fails -- we just drop back to unbuffered file
  768.              *        i/o instead.
  769.              */
  770.             LogBufferSize = LOGBUFFER_SIZE;
  771.             LogBufferPos  = 0;
  772.             LogBuffer     = AllocMem(LogBufferSize, MEMF_ANY);
  773.         }
  774.     }
  775.     LogActive = 1;
  776.     SetLogGadget(DefaultLogMode, LG_REFRESH);
  777.     UpdateStatus();
  778.     SetMenuOptions();
  779.  
  780.     DateStamp(&datetime.dat_Stamp);
  781.     datetime.dat_Format     = FORMAT_DOS;
  782.     datetime.dat_Flags     = 0;
  783.     datetime.dat_StrDay     = weekday;
  784.     datetime.dat_StrDate = datestr;
  785.     datetime.dat_StrTime = timestr;
  786.     DateToStr(&datetime);
  787.     mysprintf(startmsg, MSG(MSG_LOG_START), weekday, datestr, timestr);
  788.  
  789.     /*
  790.      *        Now select appropriate output format for log
  791.      */
  792.     LogWidth = ParseFormatString(LogFormat, LogEFormat, MAX_FORM_LEN);
  793.     if (!LogWidth)
  794.         LogWidth = ParseFormatString(BufFormat, LogEFormat, MAX_FORM_LEN);
  795.     WriteLog(startmsg);
  796.     WriteLogHeader();
  797. open_okay:
  798.     Paused = pausestate;
  799.     return (1);
  800.  
  801. open_failed:
  802.     Paused = pausestate;
  803.     return (0);
  804. }
  805.  
  806. /*
  807.  *        SaveBuffer(savetype, savename, overwrite)
  808.  *
  809.  *        This function saves the contents of some or all of the current
  810.  *        buffer to a disk file or to the clipboard.
  811.  *
  812.  *        savetype is SAVEBUF_WINDOW to save only that portion of the
  813.  *        buffer that is currently displayed in the main window, or
  814.  *        SAVEBUF_ALL to save the entire buffer.
  815.  *
  816.  *        savename is a pointer to a disk filename, or SAVEBUF_CLIPBOARD to
  817.  *        save to the clipboard.device.
  818.  *
  819.  *        overwrite is SAVEBUF_OVERWRITE to force an existing file to be
  820.  *        overwritten automatically or SAVEBUF_PROMPT to let the user
  821.  *        choose whether to overwrite using a requester. If the user
  822.  *        chooses not to overwrite, then success is returned.
  823.  *
  824.  *        Returns 1 for success or 0 for failure.
  825.  */
  826. int SaveBuffer(int savetype, char *savename, int overwrite)
  827. {
  828.     struct IOClipReq *clipreq;
  829.     struct MsgPort     *clipport;
  830.     BPTR  savefile;
  831.     UBYTE *savebuf;
  832.     Event *curev;
  833.     ULONG curpos;
  834.     int startseq;
  835.     int curseq;
  836.     int endseq;
  837.     int leftcol;
  838.     int rightcol;
  839.     int totalsize;
  840.     int width;
  841.     int numlines;
  842.  
  843.     if (savetype == SAVEBUF_WINDOW) {
  844.         /*
  845.          *        Save only visible portion of buffer
  846.          */
  847.         startseq    = TopSeq;
  848.         curev        = TopEvent;
  849.         endseq        = BottomSeq;
  850.         leftcol        = LeftCol;
  851.         rightcol    = RightCol;
  852.     } else {
  853.         /*
  854.          *        Save entire buffer
  855.          */
  856.         startseq    = RealFirstSeq;
  857.         curev        = HeadNode(&EventList);
  858.         endseq        = EndSeq;
  859.         leftcol        = 0;
  860.         rightcol    = BufferWidth - 1;
  861.     }
  862.  
  863.     savebuf = AllocMem(SAVEBUFFER_SIZE, MEMF_ANY);
  864.     if (!savebuf)
  865.         return (0);
  866.  
  867.     curpos = 0;
  868.  
  869.     /*
  870.      *        Now try and open our output device
  871.      */
  872.     if (savename == SAVEBUF_CLIPBOARD) {
  873.         /*
  874.          *        Open clipboard
  875.          */
  876.         clipport = CreateMsgPort();
  877.         if (!clipport) {
  878.             FreeMem(savebuf, SAVEBUFFER_SIZE);
  879.             return (0);
  880.         }
  881.         clipreq = (struct IOClipReq *)CreateExtIO(clipport, sizeof(*clipreq));
  882.         if (!clipreq) {
  883.             FreeMem(savebuf, SAVEBUFFER_SIZE);
  884.             DeleteMsgPort(clipport);
  885.             return (0);
  886.         }
  887.         if (OpenDevice("clipboard.device", 0, (void *)clipreq, 0)) {
  888.             FreeMem(savebuf, SAVEBUFFER_SIZE);
  889.             DeleteExtIO((struct IORequest *)clipreq);
  890.             DeleteMsgPort(clipport);
  891.             return (0);
  892.         }
  893.         clipreq->io_Error    = 0;
  894.         clipreq->io_Offset    = 0;
  895.         clipreq->io_ClipID    = 0;
  896.     } else {
  897.         /*
  898.          *        Open log file
  899.          */
  900.         int filemode = MODE_NEWFILE;;
  901.  
  902.         if (overwrite == SAVEBUF_PROMPT) {
  903.             BPTR lk = Lock(savename, ACCESS_READ);
  904.  
  905.             if (lk) {
  906.                 UnLock(lk);
  907.                 switch (GetResponse(MSG(MSG_REQ_APPEND_OVERWRITE_CANCEL),
  908.                                     MSG(MSG_REQ_FILE_EXISTS), savename)) {
  909.  
  910.                     case 1:        filemode = MODE_READWRITE;        break;
  911.                     case 2:        filemode = MODE_NEWFILE;        break;
  912.                     default:    FreeMem(savebuf, SAVEBUFFER_SIZE);
  913.                                 return (1);
  914.                 }
  915.             }
  916.         }
  917.         savefile = Open(savename, filemode);
  918.         if (!savefile) {
  919.             FreeMem(savebuf, SAVEBUFFER_SIZE);
  920.             return (0);
  921.         }
  922.         if (filemode == MODE_READWRITE) {
  923.             Seek(savefile, 0, OFFSET_END);
  924.             Write(savefile, "\n", 1);    /* Leave blank line between appends */
  925.         }
  926.     }
  927.  
  928.     /*
  929.      *        Now calculate how many lines there are to be saved in the
  930.      *        buffer. There will be as many lines as were selected from
  931.      *        startseq to endseq (inclusive) plus two additional lines for
  932.      *        the header and underline. Each line will have (rightcol-leftcol+1)
  933.      *        characters on it, plus one additional character for the newline.
  934.      *        We need to calculate all this in advance because the header
  935.      *        written for the clipboard includes the number of characters of
  936.      *        text to be written.
  937.      */
  938.     width = (rightcol - leftcol + 1);
  939.  
  940.     if (IsListEmpty(&EventList))
  941.         numlines = 2;
  942.     else
  943.         numlines = (endseq - startseq + 1) + 2;
  944.  
  945.     totalsize = (width + 1) * numlines;
  946.  
  947.     if (savename == SAVEBUF_CLIPBOARD) {
  948.         /*
  949.          *        Write the clipboard FTXT header into the buffer. We
  950.          *        need to write the amount of text, but we also need
  951.          *        to write the total size of the clip (including header)
  952.          *        rounded to a word boundary.
  953.          */
  954.         int cliplen = (totalsize + 13) & ~1;    /* Includes header */
  955.  
  956.         memcpy(savebuf, "FORM....FTXTCHRS....", 20);
  957.         *((ULONG *)&savebuf[4])  = cliplen;
  958.         *((ULONG *)&savebuf[16]) = totalsize;
  959.         curpos += 20;
  960.     }
  961.     /*
  962.      *        Next, output the two header lines (the column headings plus
  963.      *        the underline)
  964.      */
  965.     FormatEvent(BufferEFormat, NULL, savebuf + curpos, leftcol, rightcol);
  966.     curpos += width;
  967.     savebuf[curpos++] = '\n';
  968.  
  969.     UnderlineTitles(BufferEFormat, BufferLine, '-');
  970.     memcpy(savebuf + curpos, BufferLine + leftcol, width);
  971.     curpos += width;
  972.     savebuf[curpos++] = '\n';
  973.  
  974.     /*
  975.      *        Now walk through the lines in the buffer, outputting each line
  976.      *        one at a time.
  977.      */
  978.     curseq     = startseq;
  979.     totalsize -= (width + width + 2);    /* Allow for header */
  980.  
  981.     while (totalsize > 0) {
  982.         /*
  983.          *        First check if we have enough room in the buffer for
  984.          *        the next line -- if not, then flush the buffer
  985.          */
  986.         if ((curpos + width + 2) >= SAVEBUFFER_SIZE) {
  987.             if (savename == SAVEBUF_CLIPBOARD) {
  988.                 clipreq->io_Command = CMD_WRITE;
  989.                 clipreq->io_Data    = savebuf;
  990.                 clipreq->io_Length    = curpos;
  991.                 DoIO((struct IORequest *)clipreq);
  992.             } else
  993.                 Write(savefile, savebuf, curpos);
  994.             curpos = 0;
  995.         }
  996.         /*
  997.          *        The next section must be completely enclosed in
  998.          *        ObtainSemaphore/ReleaseSemaphore to ensure the
  999.          *        buffer doesn't change under our feet
  1000.          */
  1001.         ObtainSemaphore(&BufSem);
  1002.         if (curseq < RealFirstSeq) {
  1003.             /*
  1004.              *        Uhoh -- the list has scrolled past us while we
  1005.              *        were outputting it; catch up as best we can.
  1006.              */
  1007.             curev = HeadNode(&EventList);
  1008.             if (NextNode(curev))
  1009.                  curseq = curev->seqnum;
  1010.             else
  1011.                  curseq = endseq + 1;
  1012.         }
  1013.         if (curseq > endseq) {
  1014.             /*
  1015.              *        We've somehow run out of lines -- this should never
  1016.              *        happen, but you never know. For disk files, we can
  1017.              *        just avoid outputting anything else. For the
  1018.              *        clipboard, we need to output something, so we just
  1019.              *        output a line of spaces.
  1020.              */
  1021.             if (savename != SAVEBUF_CLIPBOARD)
  1022.                 break;
  1023.             memset(savebuf + curpos, ' ', width);
  1024.         } else {
  1025.             FormatEvent(BufferEFormat, curev, savebuf + curpos,
  1026.                         leftcol, rightcol);
  1027.             curev = NextNode(curev);
  1028.             curseq++;
  1029.         }
  1030.         curpos += width;
  1031.         savebuf[curpos++] = '\n';
  1032.         totalsize -= (width + 1);
  1033.         ReleaseSemaphore(&BufSem);
  1034.     }
  1035.  
  1036.     /*
  1037.      *        Now we've output the entire save region, so just flush anything
  1038.      *        left in the save buffer and we're done.
  1039.      */
  1040.     if (savename == SAVEBUF_CLIPBOARD) {
  1041.         /*
  1042.          *        Make sure out total output is word-aligned, otherwise
  1043.          *        Bad Things will happen
  1044.          */
  1045.         if (totalsize & 1)
  1046.             savebuf[curpos++] = '\0';
  1047.  
  1048.         if (curpos > 0) {
  1049.             clipreq->io_Command = CMD_WRITE;
  1050.             clipreq->io_Data    = savebuf;
  1051.             clipreq->io_Length    = curpos;
  1052.             DoIO((struct IORequest *)clipreq);
  1053.         }
  1054.         clipreq->io_Command = CMD_UPDATE;
  1055.         DoIO((struct IORequest *)clipreq); /* Make data publically available */
  1056.  
  1057.         CloseDevice((struct IORequest *)clipreq);
  1058.         DeleteExtIO((struct IORequest *)clipreq);
  1059.         DeleteMsgPort(clipport);
  1060.     } else {
  1061.         if (curpos)
  1062.             Write(savefile, savebuf, curpos);
  1063.         Close(savefile);
  1064.     }
  1065.     FreeMem(savebuf, SAVEBUFFER_SIZE);
  1066.     return (1);
  1067. }
  1068.  
  1069. /*
  1070.  *        SetMonitorMode(type)
  1071.  *
  1072.  *        Sets the current monitor mode to the specified type. This will
  1073.  *        be one of the following three:
  1074.  *
  1075.  *            MONITOR_NORMAL
  1076.  *            MONITOR_PAUSED
  1077.  *            MONITOR_DISABLED
  1078.  *
  1079.  *        Automatically takes care of updating the gadget imagery and
  1080.  *        menu options to reflect the mode. Also ignores attempts to
  1081.  *        Pause() if the main window isn't currently open. Note that
  1082.  *        the three modes are mutually exclusive.
  1083.  *
  1084.  *        The status line is also updated to reflect the current mode.
  1085.  *        The global MonitorType reflects the currently selected mode
  1086.  *        after this call.
  1087.  *
  1088.  *        Important: any call which may possible spawn a subtask
  1089.  *        (e.g. showing an ASL file requester etc.) must force the Paused
  1090.  *        variable to 0 before hand and then restore it to its original
  1091.  *        setting afterwards. This ensures that any other tasks that were
  1092.  *        already paused remain paused, but any new tasks that make calls
  1093.  *        during the activity will not be paused.
  1094.  *
  1095.  *        This isn't perfect, but it gives about 99% certainty of avoiding
  1096.  *        deadlocks, while still ensuring that any existing paused tasks
  1097.  *        (normally just one or two) remain paused. Thus, if someone has
  1098.  *        paused some stuff so they can open a log file, turn on the
  1099.  *        printer, or whatever, it will remain paused. (The possibility of
  1100.  *        deadlock comes about if our external file i/o somehow causes
  1101.  *        another process to make a call to one of the monitored SnoopDos
  1102.  *        functions, e.g. OpenLibrary or OpenDevice).
  1103.  *
  1104.  *        The alternative is to completely disable pausing on such calls, in
  1105.  *        which case vast quantities of output might be generated while the
  1106.  *        person is messing around in the file requester oblivious.
  1107.  *
  1108.  *        NOTE: It is impossible to enter PAUSED mode unless the SnoopDos
  1109.  *        window is open -- this is to prevent situations where you freeze
  1110.  *        the system (e.g. it's waiting for you to unpause, but you can't
  1111.  *        because you can't call up the main window due to something else.)
  1112.  */
  1113. void SetMonitorMode(int modetype)
  1114. {
  1115.     int paused   = 0;
  1116.     int disabled = 0;
  1117.     int changed  = 0;
  1118.  
  1119.     if (!MainWindow && modetype == MONITOR_PAUSED)
  1120.         return;
  1121.  
  1122.     if (modetype == MONITOR_DISABLED)
  1123.         disabled = 1;
  1124.     else if (modetype == MONITOR_PAUSED)
  1125.         paused = 1;
  1126.  
  1127.     /*
  1128.      *        Now update main window gadgets if necessary
  1129.      */
  1130.     if (MainWindow) {
  1131.         if (paused != Paused) {
  1132.             ShowGadget(MainWindow, Gadget[GID_PAUSE],
  1133.                        (paused ? GADGET_DOWN : GADGET_UP));
  1134.         }
  1135.         if (disabled != Disabled) {
  1136.             ShowGadget(MainWindow, Gadget[GID_DISABLE],
  1137.                        (disabled ? GADGET_DOWN : GADGET_UP));
  1138.         }
  1139.     }
  1140.  
  1141.     /*
  1142.      *        Next,  make the changes actually take effect.
  1143.      */
  1144.     if (paused != Paused) {
  1145.         DateStamp(&PauseDateStamp);
  1146.         if (paused)
  1147.             ObtainSemaphore(&PauseSem);
  1148.         else
  1149.             ReleaseSemaphore(&PauseSem);
  1150.         Paused  = paused;
  1151.         changed = 1;
  1152.     }
  1153.     if (disabled != Disabled) {
  1154.         DateStamp(&DisableDateStamp);
  1155.         Disabled = disabled;
  1156.         changed  = 1;
  1157.         LoadFuncSettings(&CurSettings.Func);    /* Update patches state */
  1158.  
  1159.         if (LogActive) {
  1160.             char msg[100];
  1161.  
  1162.             mysprintf(msg, MSG(Disabled ? MSG_LOG_DISABLED : MSG_LOG_ENABLED),
  1163.                            GetTimeStr(NULL));
  1164.             WriteLog(msg);
  1165.             WriteLog(NULL);    /* Flush output to keep everything up-to-date */
  1166.         }
  1167.     }
  1168.     if (changed) {
  1169.         if (LogActive)        /* Flush log file when Paused */
  1170.             WriteLog(NULL);
  1171.         UpdateStatus();
  1172.         SetMenuOptions();
  1173.     }
  1174.     MonitorType = modetype;
  1175. }
  1176.  
  1177. /*
  1178.  *        SingleStep()
  1179.  *
  1180.  *        Undoes Pause just long enough for all currently waiting tasks
  1181.  *        to execute a single monitored event. Usually, this will just
  1182.  *        produce a single event in the event buffer.
  1183.  *
  1184.  *        Why would you want to do this? Because it's a real handy way
  1185.  *        of single-stepping through a program's actions at startup,
  1186.  *        especially if it happens to be crashing after doing a particular
  1187.  *        action!
  1188.  *
  1189.  *        We implement the singlestep by freeing up the Pause semaphore
  1190.  *        and then immediately trying to recapture it. We rely on the
  1191.  *        fact that Exec schedules semaphore operations on a strictly
  1192.  *        first-come first-served basis, so as soon as we free it,
  1193.  *        all the waiting tasks are guaranteed to run before we can
  1194.  *        recapture it. We move ourselves to a high priority temporarily
  1195.  *        to ensure that nobody gets a chance to run two events before
  1196.  *        we can regain control again.
  1197.  *
  1198.  *        If we weren't in pause mode before, we will be when we exit.
  1199.  *
  1200.  */
  1201. void SingleStep(void)
  1202. {
  1203.     int oldpri;
  1204.  
  1205.     if (!MainWindow)
  1206.         return;
  1207.  
  1208.     if (!Paused)
  1209.         SetMonitorMode(MONITOR_PAUSED);
  1210.     
  1211.     WriteLog(NULL);        /* Make sure logfile is up to date! */
  1212.  
  1213.     /*
  1214.      *        To ensure that we get the semaphore back as soon as we 
  1215.      *        possibly can (i.e. let all the waiting tasks run but nobody
  1216.      *        else), we bump our priority up temporarily
  1217.      */
  1218.     oldpri = SetTaskPri(SysBase->ThisTask, 25);
  1219.     ReleaseSemaphore(&PauseSem);
  1220.     ObtainSemaphore(&PauseSem);
  1221.     SetTaskPri(SysBase->ThisTask, oldpri);
  1222. }
  1223.  
  1224. /*
  1225.  *        CheckForScreen()
  1226.  *
  1227.  *        Checks if we've already got a SnoopDos screen open -- if we do,
  1228.  *        then returns TRUE. If we don't, then tries to lock the current
  1229.  *        screen and returns TRUE. If we can't lock the screen, then
  1230.  *        displays an error message and returns FALSe.
  1231.  */
  1232. int CheckForScreen(void)
  1233. {
  1234.     if (!SnoopScreen) {
  1235.         SetupScreen();
  1236.         if (!SnoopScreen) {
  1237.             ShowError(MSG(MSG_ERROR_NO_SCREEN));
  1238.             return (FALSE);
  1239.         }
  1240.     }
  1241.     return (TRUE);
  1242. }
  1243.  
  1244. /*
  1245.  *        ShowSnoopDos()
  1246.  *
  1247.  *        Attempts to open SnoopDos on the user's current preferred screen.
  1248.  *        If SnoopDos is already running, but on a different screen, then
  1249.  *        it is moved to this screen. Any open SnoopDos windows are moved,
  1250.  *        though any AmigaGuide help remains on the previous screen until
  1251.  *        the next time help is requested.
  1252.  *
  1253.  *        Fails if we can't lock the new screen for some reason (this
  1254.  *        should never happen!)
  1255.  */
  1256. int ShowSnoopDos(void)
  1257. {
  1258.     struct Screen *prevscr  = SnoopScreen;
  1259.     struct Window *prevfunc = FuncWindow;
  1260.     struct Window *prevform = FormWindow;
  1261.     struct Window *prevset  = SetWindow;
  1262.  
  1263.     if (!SetupScreen()) {
  1264.         ShowError(MSG(MSG_ERROR_NO_SCREEN));
  1265.         return (FALSE);
  1266.     }
  1267.     RemoveProgramFromWorkbench();
  1268.     ScreenToFront(SnoopScreen);
  1269.     if (prevscr == SnoopScreen) {
  1270.         /*
  1271.          *        We were already running, and we're on the
  1272.          *        same screen, so nothing else to do. For good
  1273.          *        measure, we bring the main window to the
  1274.          *        foreground, however.
  1275.          */
  1276.         OpenMainWindow();
  1277.         return (TRUE);
  1278.     }
  1279.     if (prevscr != NULL) {
  1280.         /*
  1281.          *        We're moving from a previous screen to this new screen,
  1282.          *        so bring all the windows with us
  1283.          */
  1284.         CleanupSubWindows();
  1285.         CloseMainWindow();
  1286.         OpenMainWindow();
  1287.         if (prevset)    OpenSettingsWindow();
  1288.         if (prevfunc)    OpenFunctionWindow();
  1289.         if (prevform)    OpenFormatWindow();
  1290.     } else {
  1291.         /*
  1292.          *        We're opening on an entirely new screen -- if
  1293.          *        DisableOnHide is true, then we need to restore
  1294.          *        the saved state of Disable/Enable (since it will
  1295.          *        have been disabled while in the background)
  1296.          */
  1297.         if (DisableOnHide)
  1298.             SetMonitorMode(LastKnownState);
  1299.         OpenMainWindow();
  1300.     }
  1301.     return (TRUE);
  1302. }
  1303.  
  1304. /*
  1305.  *        HideSnoopDos()
  1306.  *
  1307.  *        Removes all SnoopDos windows from the screen. If DisableOnHide
  1308.  *        is true, then SnoopDos is put into a disabled state. Otherwise,
  1309.  *        if we're currently paused, then SnoopDos is unpaused (since
  1310.  *        otherwise, it would be easy to get into a situation where
  1311.  *        everything was frozen and you could do nothing to bring
  1312.  *        SnoopDos back to life).
  1313.  *
  1314.  *        If commodities library hasn't been opened, then we don't hide
  1315.  *        since that would make it difficult to resurrect SnoopDos again.
  1316.  *        However, if commodities library is open but the user has given
  1317.  *        an invalid hotkey, we do hide -- they can always use commodities
  1318.  *        exchange to reactivate it.
  1319.  */
  1320. void HideSnoopDos(void)
  1321. {
  1322.     LastKnownState = MonitorType;
  1323.  
  1324.     if (CurSettings.Setup.HideMethod == HIDE_NONE)
  1325.         return;
  1326.  
  1327.     CleanupSubWindows();
  1328.     CloseMainWindow();
  1329.     CleanupScreen();
  1330.     if (DisableOnHide)
  1331.         SetMonitorMode(MONITOR_DISABLED);
  1332.     else if (Paused)
  1333.         SetMonitorMode(MONITOR_NORMAL);
  1334.     if (CurSettings.Setup.HideMethod != HIDE_INVIS)
  1335.         AddProgramToWorkbench(CurSettings.Setup.HideMethod);
  1336. }
  1337.  
  1338. /*
  1339.  *        InitMenus()
  1340.  *
  1341.  *        Should be called once as soon as the language file has been
  1342.  *        read in. Walks down the menu structure and replaces the
  1343.  *        message ID's with actual pointers to the message.
  1344.  */
  1345. void InitMenus(void)
  1346. {
  1347.     struct NewMenu *menu;
  1348.  
  1349.     for (menu = &MainMenu[0]; menu->nm_Type != NM_END; menu++) {
  1350.         if (menu->nm_Label && menu->nm_Label != NM_BARLABEL) {
  1351.             char *msg = MSG((ULONG)(menu->nm_Label));
  1352.  
  1353.             menu->nm_Label = msg + 2;
  1354.             if (*msg && *msg != ' ')
  1355.                 menu->nm_CommKey = msg;
  1356.         }
  1357.     }
  1358. }
  1359.  
  1360. /*
  1361.  *        CalcMinMainSize(gadgetfa, bufferfa, &width, &height)
  1362.  *
  1363.  *        Fills in width and height with the minimum allowable dimensions
  1364.  *        the main window can obtain using the specified gadget and buffer
  1365.  *        fonts. Returns TRUE for success, FALSE for failure (e.g. window
  1366.  *        to big to fit on screen using current fonts).
  1367.  */
  1368. BOOL CalcMinMainSize(struct TextAttr *gadgetfa, struct TextAttr *bufferfa,
  1369.                      int *pwidth, int *pheight)
  1370. {
  1371.     struct TextFont *gfont;
  1372.     struct TextFont *bfont;
  1373.     int widthleft;
  1374.     int widthright;
  1375.     int width;
  1376.     int height;
  1377.     int fonty;
  1378.     int bfonty;
  1379.     int bfontx;
  1380.     int bordright = SizeImage->Width;
  1381.     int bordbot   = SizeImage->Height;
  1382.  
  1383.     gfont = MyOpenFont(gadgetfa);
  1384.     if (!gfont)
  1385.         return (FALSE);
  1386.  
  1387.     bfont = MyOpenFont(bufferfa);
  1388.     if (!bfont) {
  1389.         CloseFont(gfont);
  1390.         return (FALSE);
  1391.     }
  1392.     if (bfont->tf_Flags & FPF_PROPORTIONAL) {
  1393.         CloseFont(bfont);
  1394.         CloseFont(gfont);
  1395.         return (FALSE);
  1396.     }
  1397.     fonty        = gfont->tf_YSize;
  1398.     bfonty        = bfont->tf_YSize;
  1399.     bfontx      = bfont->tf_XSize;
  1400.  
  1401.     widthleft   = MaxTextLen(gfont, MainWidthLeftText)  + 12;
  1402.     widthright  = MaxTextLen(gfont, MainWidthRightText) + 12;
  1403.  
  1404.     width  = (widthleft + widthright + MAIN_MARGIN) * 2 +
  1405.              BorderLeft + bordright;
  1406.     height = TitlebarHeight + (bfonty + BoxInterGap) * 3 +
  1407.              bordbot + 20 + (fonty + 10) * 3;
  1408.  
  1409.     CloseFont(bfont);
  1410.     CloseFont(gfont);
  1411.  
  1412.     if (width > ScreenWidth || height > ScreenHeight)
  1413.         return (FALSE);
  1414.  
  1415.     *pwidth  = width;
  1416.     *pheight = height;
  1417.     return (TRUE);
  1418. }
  1419.  
  1420. /*
  1421.  *        SetupBufferRastPort()
  1422.  *
  1423.  *        Sets up the main window rastport to have the correct font and
  1424.  *        colours for rendering. Should always be called before OutputBufLine
  1425.  *        if gadget operations may have occurred since the last redraw.
  1426.  */
  1427. void SetupBufferRastPort(void)
  1428. {
  1429.     struct RastPort *rport = MainWindow->RPort;
  1430.  
  1431.     SetAPen(rport, 1);
  1432.     SetBPen(rport, 0);
  1433.     SetDrMd(rport, JAM2);
  1434.     SetFont(rport, BufferFont);
  1435. }
  1436.  
  1437. /*
  1438.  *        DrawHeaderLine()
  1439.  *
  1440.  *        Redraws the current event header line in the top part of the window
  1441.  *
  1442.  *        If a column is currently selected for dragging, then draws the
  1443.  *        column heading in white to indicate it's active.
  1444.  */
  1445. void DrawHeaderLine(void)
  1446. {
  1447.     struct RastPort *rport = MainWindow->RPort;
  1448.     int numcols = RightCol - LeftCol + 1;
  1449.  
  1450.     if (ClearMainRHS)
  1451.         numcols = BoxCols;
  1452.  
  1453.     SetupBufferRastPort();
  1454.     FormatEvent(BufferEFormat, NULL, BufferLine, LeftCol, LeftCol+numcols-1);
  1455.  
  1456.     /*
  1457.      *        We mark the very last position on the header line with a single
  1458.      *        dot, which can then be grabbed by the user to resize the width
  1459.      *        of the last column. We don't overwrite the actual text of the
  1460.      *        final header, if it's narrow.
  1461.      *
  1462.      *        If the columns are currently being dragged, we display a | instead
  1463.      *        of a dot, to make it easier to spot, and we display it even if
  1464.      *        it would overwrite some of the final heading.
  1465.      */
  1466.     if (DraggingColumn) {
  1467.         /*
  1468.          *        Draw the currently selected column with a white heading,
  1469.          *        and the other stuff with a black heading
  1470.          */
  1471.         int selectwidth = SelectEvent->width;
  1472.  
  1473.         if ((SelectEvent+1)->type != EF_END) {
  1474.             /*
  1475.              *        If we're not highlighting the last column already
  1476.              *        then indicate the end of the last column with a
  1477.              *        little vertical bar
  1478.              */
  1479.             BufferLine[BufferWidth - LeftCol - 1] = '|';
  1480.         }
  1481.         if (SelectStartCol > LeftCol) {
  1482.             /*
  1483.              *        Draw black portion to left of selected column
  1484.              */
  1485.             int width = MIN(SelectStartCol - LeftCol, numcols);
  1486.  
  1487.             Move(rport, BoxInLeft, BoxHeaderLine);
  1488.             Text(rport, BufferLine, width);
  1489.         }
  1490.         if ((SelectStartCol + selectwidth) < (LeftCol + numcols)) {
  1491.             /*
  1492.              *        Draw black portion to right of selected column
  1493.              */
  1494.             int xoffset = SelectStartCol + selectwidth - LeftCol;
  1495.  
  1496.             if (xoffset < 0)
  1497.                 xoffset = 0;
  1498.  
  1499.             Move(rport, BoxInLeft + BoxCharWidth * xoffset, BoxHeaderLine);
  1500.             Text(rport, BufferLine + xoffset, numcols - MAX(xoffset,0));
  1501.         }
  1502.         if (SelectStartCol >= LeftCol && SelectStartCol < (LeftCol+numcols)) {
  1503.             /*
  1504.              *        Draw selected column heading in white
  1505.              */
  1506.             int xoffset = SelectStartCol - LeftCol;
  1507.             int fwidth  = MIN(selectwidth, numcols - xoffset);
  1508.  
  1509.             if (fwidth > 0) {
  1510.                 SetAPen(rport, 2);
  1511.                 Move(rport, BoxInLeft  + xoffset * BoxCharWidth,
  1512.                      BoxHeaderLine);
  1513.                 Text(rport, BufferLine + xoffset, fwidth);
  1514.                 SetAPen(rport, 1);
  1515.             }
  1516.         } else if (SelectStartCol < LeftCol &&
  1517.                    (SelectStartCol + selectwidth) >= LeftCol) {
  1518.             /*
  1519.              *        Draw portion of selected column heading in white
  1520.              */
  1521.             int colwidth = SelectStartCol + selectwidth - LeftCol;
  1522.  
  1523.             SetAPen(rport, 2);
  1524.             Move(rport, BoxInLeft, BoxHeaderLine);
  1525.             Text(rport, BufferLine, MIN(colwidth, numcols));
  1526.             SetAPen(rport, 1);
  1527.         }
  1528.     } else {
  1529.         /*
  1530.          *        Not dragging a column, so draw entire line in black
  1531.          */
  1532.         Move(rport, BoxInLeft, BoxHeaderLine);
  1533.         Text(rport, BufferLine, numcols);
  1534.     }
  1535. }
  1536.  
  1537. /*
  1538.  *        RedrawMainWindow()
  1539.  *
  1540.  *        Redraws all the main parts of the window, except gadgets.
  1541.  */
  1542. void RedrawMainWindow(void)
  1543. {
  1544.     struct RastPort *rport;
  1545.     
  1546.     if (!MainWindow)
  1547.         return;
  1548.  
  1549.     rport = MainWindow->RPort;
  1550.     DrawBevelBox(rport, BoxLeft, MainWindow->BorderTop,
  1551.                         BoxWidth, BoxTop - MainWindow->BorderTop,
  1552.                         GT_VisualInfo,    MainVI,
  1553.                         TAG_DONE);
  1554.     DrawBevelBox(rport, BoxLeft, BoxTop, BoxWidth, BoxHeight,
  1555.                         GT_VisualInfo,    MainVI,
  1556.                         TAG_DONE);
  1557.     /*
  1558.      *        We only draw the third bevel box if either the status line or
  1559.      *        gadget line is enabled.
  1560.      */
  1561.     if (StatusLine || GadgetsLine) {
  1562.         DrawBevelBox(rport, BoxLeft, BoxTop + BoxHeight, BoxWidth,
  1563.                             MainWindow->Height - MainWindow->BorderBottom -
  1564.                                                  BoxTop - BoxHeight,
  1565.                             GT_VisualInfo,    MainVI,
  1566.                             TAG_DONE);
  1567.     }
  1568.     DrawHeaderLine();
  1569.     ShowBuffer(TopSeq, DISPLAY_ALL);
  1570.     if (ClearMainRHS) {
  1571.         if ((RightCol-LeftCol+1) < BoxCols) {
  1572.             /*
  1573.              *        Erase strip to right of our rendered text
  1574.              */
  1575.             SetAPen(rport, 0);
  1576.             SetDrMd(rport, JAM1);
  1577.             RectFill(rport, BoxInLeft + (RightCol-LeftCol+1) * BoxCharWidth,
  1578.                             BoxInTop,
  1579.                             BoxInLeft + BoxInWidth - 1,
  1580.                             BoxInTop + BoxInHeight - 1);
  1581.         }
  1582.         ClearMainRHS = 0;
  1583.     }
  1584. }
  1585.  
  1586. /*
  1587.  *        SetTextSpacing(newspacing)
  1588.  *
  1589.  *        Updates the spacing of the main window to reflect the new spacing
  1590.  *        value passed in, and adjust all the global variables accordingly.
  1591.  */
  1592. void SetTextSpacing(int newspacing)
  1593. {
  1594.     int bfonty          = BufferFont->tf_YSize;
  1595.     int bfontbaseline = BufferFont->tf_Baseline;
  1596.     int maxheight;
  1597.  
  1598.     if (BoxInterGap == newspacing)
  1599.         return;        /* No change needed */
  1600.     
  1601.     ChangedSpacing = 1;            /* Flag for resize routine */
  1602.     BoxInterGap    = newspacing;
  1603.  
  1604.     if (!MainWindow)
  1605.         return;
  1606.  
  1607.     SetAPen(MainWindow->RPort, 0);
  1608.     SetDrMd(MainWindow->RPort, JAM1);
  1609.     RectFill(MainWindow->RPort, BoxInLeft, BoxInTop,
  1610.                                 BoxInLeft + BoxInWidth - 1,
  1611.                                 BoxInTop + BoxInHeight - 1);
  1612.     
  1613.     maxheight    = BoxHeight - VBorderGap * 2 - 2;
  1614.     BoxSpacing    = bfonty + BoxInterGap;
  1615.     BoxRows        = (maxheight + BoxInterGap) / BoxSpacing;
  1616.     BoxInHeight    = BoxRows * BoxSpacing - BoxInterGap;
  1617.     BoxInTop    = BoxTop   + VBorderGap + 1;
  1618.     BoxBaseline    = BoxInTop + bfontbaseline;
  1619.  
  1620.     /*
  1621.      *        We now check to see if we're currently at the end of the buffer,
  1622.      *        and if so, make sure we stay that way by repositioning after the
  1623.      *        line change.
  1624.      */
  1625.     if (BottomSeq >= EndSeq)
  1626.         ShowBuffer(EndSeq, DISPLAY_NONE);
  1627.         
  1628.     LOCK_LAYERS;
  1629.     RedrawMainWindow();
  1630.     UNLOCK_LAYERS;
  1631.     UpdateMainVScroll();
  1632. }
  1633.     
  1634. /*
  1635.  *        InitMainMargins()
  1636.  *
  1637.  *        Initialises the window margins according to the currently selected
  1638.  *        left offset and the current buffer width (as determined by the
  1639.  *        format string). Automatically ensures margins are kept within
  1640.  *        the bounds of the displayable area, adjusting them as necessary.
  1641.  *
  1642.  *        On entry, LeftCol should contain the current left margin, BoxCols
  1643.  *        should be initialised to the current width of the displayable
  1644.  *        buffer area, and BufferWidth should be the maximum width of the
  1645.  *        currently selected format string.
  1646.  */
  1647. void InitMainMargins(void)
  1648. {
  1649.     if ((LeftCol + BoxCols) > BufferWidth)
  1650.         LeftCol = BufferWidth - BoxCols;
  1651.  
  1652.     if (LeftCol < 0)
  1653.         LeftCol = 0;
  1654.  
  1655.     RightCol = LeftCol + BoxCols - 1;
  1656.     if (RightCol >= BufferWidth) {
  1657.         RightCol = BufferWidth - 1;
  1658.         if (RightCol < 0)
  1659.             RightCol = 0;
  1660.     }
  1661. }
  1662.  
  1663. /*
  1664.  *        FreeScrollGadgets()
  1665.  *
  1666.  *        Frees all associated BOOPSI objects and images used by the
  1667.  *        scroll gadgets. Must only be called after the main window
  1668.  *        has closed.
  1669.  */
  1670. void FreeScrollGadgets(void)
  1671. {
  1672.     int i;
  1673.  
  1674.     for (i = GID_STARTSCROLL; i <= GID_ENDSCROLL; i++) {
  1675.         if (Gadget[i]) {
  1676.             DisposeObject(Gadget[i]);
  1677.             Gadget[i] = NULL;
  1678.         }
  1679.     }
  1680.     for (i = 0; i < 4; i++) {
  1681.         if (ScrollImage[i]) {
  1682.             DisposeObject(ScrollImage[i]);
  1683.             ScrollImage[i] = NULL;
  1684.         }
  1685.     }
  1686. }
  1687.  
  1688. /*
  1689.  *        FreeMainGadgets()
  1690.  *
  1691.  *        Frees all the gadgets allocated for the main window.
  1692.  */
  1693. void FreeMainGadgets(void)
  1694. {
  1695.     if (BufferFont) {
  1696.         CloseFont(BufferFont);
  1697.         BufferFont  = NULL;
  1698.     }
  1699.     if (MainGadList) {
  1700.         FreeGadgets(MainGadList);
  1701.         MainGadList = NULL;
  1702.     }
  1703. }
  1704.  
  1705. /*
  1706.  *        CreateScrollGadgets()
  1707.  *
  1708.  *        Creates the list of six gadgets that are used to give our window
  1709.  *        scroll bars and scroll arrows. These have to be created independently
  1710.  *        of the GadTools button gadgets, because since they are in the border,
  1711.  *        they must be added to the window at the time it is opened, and not
  1712.  *        afterwards.
  1713.  *
  1714.  *        The GadTools gadgets are removed and then re-added (with new
  1715.  *        size-sensitive positions) whenever the window is resized; if we
  1716.  *        try and do this with the BOOPSI border gadgets however, Intuition
  1717.  *        gets the positioning subtely wrong the second and subsequent times.
  1718.  *
  1719.  *        Hence you must call this function before opening the main window. All
  1720.  *        the gadgets are created relative to the current window dimensions, and
  1721.  *        so do not depend on specific hardcoded with/height values.
  1722.  *
  1723.  *        Returns a pointer to the gadget list, or NULL if the gadgets
  1724.  *        couldn't be created.
  1725.  */
  1726. struct Gadget *CreateScrollGadgets(void)
  1727. {
  1728.     struct Gadget *gadlist    = NULL;
  1729.     struct Gadget *gad        = (struct Gadget *)&gadlist;
  1730.     int sizew    = SizeImage->Width;
  1731.     int sizeh    = SizeImage->Height;
  1732.     int bw      = (ScreenResolution == SYSISIZE_LOWRES) ? 1 : 2;
  1733.     int i;
  1734.  
  1735.     /*
  1736.      *        First, create the arrows for the scroll bars. Gad starts off
  1737.      *        pointing to gadlist, which will receive the pointer to the
  1738.      *        first gadget we create.
  1739.      */
  1740.     ScrollArrows[0].widthoffset  =
  1741.     ScrollArrows[1].widthoffset  = 1 - sizew;
  1742.     ScrollArrows[2].heightoffset =
  1743.     ScrollArrows[3].heightoffset = 1 - sizeh;
  1744.  
  1745.     for (i = 0; i < 4; i++) {
  1746.         struct ScrollData *scd = &ScrollArrows[i];
  1747.         int gadid = scd->gadgetid;
  1748.         struct Image *img;
  1749.  
  1750.         img = (struct Image *)
  1751.                 NewObject(NULL,    "sysiclass",
  1752.                           SYSIA_DrawInfo,    ScreenDI,
  1753.                           SYSIA_Which,        scd->imagetype,
  1754.                           SYSIA_Size,        ScreenResolution,
  1755.                           TAG_END);
  1756.         if (!img)
  1757.             goto sgad_failed;
  1758.         ScrollImage[i] = img;
  1759.  
  1760.         gad = (struct Gadget *)
  1761.                 NewObject(NULL,                "buttongclass",
  1762.                           GA_ID,            gadid,
  1763.                           GA_Immediate,        TRUE,
  1764.                           GA_Image,            img,
  1765.                           GA_Width,            img->Width,
  1766.                           GA_Height,        img->Height,
  1767.                           GA_Previous,        gad,
  1768.                           GA_RelVerify,        TRUE,
  1769.                           scd->borderpos,    TRUE,
  1770.                           GA_RelRight,        scd->widthscale * img->Width +
  1771.                                               scd->widthoffset,
  1772.                           GA_RelBottom,        scd->heightscale * img->Height +
  1773.                                               scd->heightoffset,
  1774.                           ICA_TARGET,        ICTARGET_IDCMP,
  1775.                           TAG_END);
  1776.         if (!gad)
  1777.             goto sgad_failed;
  1778.         Gadget[gadid] = gad;
  1779.     }
  1780.     /*
  1781.      *        Now create our two BOOPSI scroll gadgets in the window border
  1782.      */
  1783.     gad = (struct Gadget *)NewObject(NULL,                 "propgclass",
  1784.                                      GA_ID,                GID_HSCROLLER,
  1785.                                      PGA_Freedom,        FREEHORIZ,
  1786.                                      PGA_NewLook,        TRUE,
  1787.                                      PGA_Borderless,    TRUE,
  1788.                                      PGA_Top,            LeftCol,
  1789.                                      PGA_Visible,        BoxCols,
  1790.                                      PGA_Total,            BufferWidth,
  1791.                                      GA_Left,            3,
  1792.                                      GA_RelBottom,        3 - sizeh,
  1793.                                      GA_RelWidth,        -sizew - 5 -
  1794.                                                         ScrollImage[0]->Width -
  1795.                                                         ScrollImage[1]->Width,
  1796.                                      GA_Height,            sizeh - 4,
  1797.                                      GA_Previous,        gad,
  1798.                                      GA_BottomBorder,    TRUE,
  1799.                                      ICA_TARGET,        ICTARGET_IDCMP,
  1800.                                      ICA_MAP,            RZ_MapTags,
  1801.                                      TAG_END);
  1802.     if (!gad)
  1803.         goto sgad_failed;
  1804.     Gadget[GID_HSCROLLER] = gad;
  1805.  
  1806.     gad = (struct Gadget *)NewObject(NULL,                 "propgclass",
  1807.                                      GA_ID,                GID_VSCROLLER,
  1808.                                      PGA_Freedom,        FREEVERT,
  1809.                                      PGA_NewLook,        TRUE,
  1810.                                      PGA_Borderless,    TRUE,
  1811.                                      PGA_Top,            TopSeq - FirstSeq,
  1812.                                      PGA_Visible,        BoxRows,
  1813.                                      PGA_Total,            EndSeq - FirstSeq + 1,
  1814.                                      GA_RelRight,        bw - sizew + 3,
  1815.                                      GA_Top,            TitlebarHeight + 1,
  1816.                                      GA_Width,            sizew - bw - bw - 4,
  1817.                                      GA_RelHeight,        -TitlebarHeight -
  1818.                                                         ScrollImage[2]->Height-
  1819.                                                         ScrollImage[3]->Height-
  1820.                                                         sizeh - 2,
  1821.                                      GA_RightBorder,    TRUE,
  1822.                                      GA_Previous,        gad,
  1823.                                      ICA_TARGET,        ICTARGET_IDCMP,
  1824.                                      ICA_MAP,            RZ_MapTags,
  1825.                                      TAG_END);
  1826.     if (!gad)
  1827.         goto sgad_failed;
  1828.     Gadget[GID_VSCROLLER] = gad;
  1829.     return (gadlist);
  1830.  
  1831. sgad_failed:
  1832.     FreeScrollGadgets();
  1833.     return (NULL);
  1834. }
  1835.  
  1836. /*
  1837.  *        CreateMainGadgets(width, height, statusline)
  1838.  *
  1839.  *        Creates the gadgets for the main SnoopDos window, assuming a window
  1840.  *        of size width x height. MainGadList will point to the list of
  1841.  *        gadgets when completed. Returns NULL if unsuccessful, or MainGadList
  1842.  *        if successful.
  1843.  *
  1844.  *        Statusline is a flag that says whether or not the status gadget is
  1845.  *        to be created.
  1846.  *
  1847.  *        There are basically seven possible gadget layouts:
  1848.  *
  1849.  *            - 4 x 3 grid with status line on top
  1850.  *            - 4 x 2 grid with no status line
  1851.  *            - 6 x 2 grid with status line occupying the upper 4 left spaces
  1852.  *            - 8 x 1 grid with no status line
  1853.  *            - 12 x 1 grid with status line
  1854.  *          - 0 grid with no gadgets or status line
  1855.  *          - 1 line grid with status line but no gadgets
  1856.  *
  1857.  *        The first two are used when the window is narrow; the second two
  1858.  *        when the window is wider. We use colwidth[0] to hold the width
  1859.  *        of the narrow gadgets, colwidth[1] for the wide gadgets, and
  1860.  *        colwidth[2] for the status line gadget.
  1861.  *
  1862.  *        To allow us to conveniently replace one gadget definition with
  1863.  *        another, we use the concept of invisible buttons -- these are
  1864.  *        like normal buttons, but their X position is set way off to the
  1865.  *        left of the window where they won't be displayed; to reveal
  1866.  *        the buttons, we simply restore their X position and make the
  1867.  *        X position of a different button negative.
  1868.  *
  1869.  *        Finally, we have toggle buttons which flip flop between on and off.
  1870.  *        These are defined as MAIN_NARROW_TOGGLE.
  1871.  */
  1872. struct Gadget *CreateMainGadgets(struct TextAttr *gadgetfa,
  1873.                                  struct TextAttr *bufferfa,
  1874.                                  int width, int height, int statusline)
  1875. {
  1876.     struct MainGadgets *mg;
  1877.     struct NewGadget ng;
  1878.     struct TextFont *font;
  1879.     struct Gadget *gadlist;
  1880.     struct Gadget *gad;
  1881.     UWORD colwidth[MAIN_NUMWIDTHS];
  1882.     WORD  colpos[12];
  1883.     UWORD spacing;                /* Spacing between gadget tops                */
  1884.     UWORD yoffset;                /* Offset of first gadget top in window        */
  1885.     int fonty;                    /* Y height of gadget font                    */
  1886.     int bfontx;                    /* X height of buffer font                    */
  1887.     int bfonty;                    /* Y height of buffer font                    */
  1888.     int bfontbaseline;            /* Baseline of buffer font                    */
  1889.     int innerwidth;                /* Usable width of window                    */
  1890.     int extraspace;                /* Spare space inbetween buffer and gadgets    */
  1891.     int gheight;                /* Height of a single gadget                */
  1892.     int width4;                    /* Minimum width needed for 4 x 3 gadgets    */
  1893.     int width6;                    /* Minimum width needed for 6 x 2 gadgets    */
  1894.     int width8;                    /* Minimum width needed for 8 x 1 gadgets    */
  1895.     int width12;                /* Minimum width needed for 12 x 1 gadgets    */
  1896.     int gridtype;                /* Grid type for gadget layout                */
  1897.     int numrows;                /* Number of rows of gadgets involved        */
  1898.     int gadg_gap;                /* Aspect-corrected gap between gadget rows    */
  1899.     int margin = MAIN_MARGIN;    /* Gadget margin at left edge of window        */
  1900.     int boxmaxwidth;            /* Temporary width of listview box            */
  1901.     int boxheight;                /* Temporary height of listview box            */
  1902.  
  1903.     int bordright  = SizeImage->Width;
  1904.     int bordbot    = SizeImage->Height;
  1905.  
  1906. /*
  1907.  *        Quick defines for our internal grid arrangements
  1908.  */
  1909. #define FOUR_BY_THREE    0
  1910. #define SIX_BY_TWO        1
  1911. #define EIGHT_BY_ONE    2
  1912. #define TWELVE_BY_ONE    3
  1913.  
  1914.     if (MainGadList)
  1915.         FreeMainGadgets();
  1916.  
  1917.     if (!MainVI) {
  1918.         MainVI = GetVisualInfoA(SnoopScreen, NULL);
  1919.         if (!MainVI)
  1920.             return (NULL);
  1921.     }
  1922.     font = MyOpenFont(gadgetfa);
  1923.     if (!font)
  1924.         return (NULL);
  1925.  
  1926.     BufferFont = MyOpenFont(bufferfa);
  1927.     if (!BufferFont) {
  1928.         CloseFont(font);
  1929.         return (NULL);
  1930.     }
  1931.     fonty            = font->tf_YSize;
  1932.     bfontx            = BufferFont->tf_XSize;
  1933.     bfonty            = BufferFont->tf_YSize;
  1934.     bfontbaseline    = BufferFont->tf_Baseline;
  1935.  
  1936.     if (SquareAspect) {
  1937.         VBorderGap   = 2;
  1938.         gadg_gap     = 3;
  1939.     } else {
  1940.         VBorderGap   = 1;
  1941.         gadg_gap     = 2;
  1942.     }
  1943.  
  1944.     gheight        = fonty + GadgetHeight;
  1945.     spacing     = gheight + gadg_gap;
  1946.  
  1947.     colwidth[MAIN_NARROW]         = MaxTextLen(font, MainWidthLeftText)  + 12;
  1948.     colwidth[MAIN_WIDE]             = MaxTextLen(font, MainWidthRightText) + 12;
  1949.  
  1950.     /*
  1951.      *        First, check if window is wide enough for gadgets. If not,
  1952.      *        then fail.
  1953.      */
  1954.     innerwidth = width - BorderLeft - bordright;
  1955.  
  1956.     if ((colwidth[MAIN_NARROW]+colwidth[MAIN_WIDE]+margin) * 2 > innerwidth) {
  1957.         CloseFont(font);
  1958.         CloseFont(BufferFont);
  1959.         BufferFont = NULL;
  1960.         return (NULL);
  1961.     }
  1962.  
  1963.     /*
  1964.      *        Now calculate how many columns our fake listview can hold using
  1965.      *        the current font, and use this to adjust our margins accordingly.
  1966.      *
  1967.      *        The '4' in boxmaxwidth comes from 2 pixels for the left and right
  1968.      *        of the box (the borders)
  1969.      */
  1970.     boxmaxwidth    = innerwidth - 2*BOX_LEFT_MARGIN - 4;
  1971.     BoxCols        = (boxmaxwidth / bfontx);
  1972.  
  1973.     width4        = colwidth[MAIN_NARROW] * 2 + colwidth[MAIN_WIDE] * 2;
  1974.     width6        = colwidth[MAIN_NARROW] * 4 + colwidth[MAIN_WIDE] * 2;
  1975.     width8        = colwidth[MAIN_NARROW] * 4 + colwidth[MAIN_WIDE] * 4;
  1976.     width12        = colwidth[MAIN_NARROW] * 8 + colwidth[MAIN_WIDE] * 4;
  1977.     colpos[0]    = BorderLeft + margin;
  1978.     innerwidth -= margin * 2;
  1979.  
  1980.     if (GadgetsLine) {
  1981.         if (statusline && width12 <= innerwidth) {
  1982.             /*
  1983.              *        Setup layout for 12 x 1 grid
  1984.              */
  1985.             int totalspace = innerwidth - width12;
  1986.             int i;
  1987.  
  1988.             for (i = 1; i < 8; i++)
  1989.                 colpos[i] = colpos[0] + colwidth[MAIN_NARROW] * i
  1990.                                       + (totalspace*i)/11;
  1991.             for (i = 8; i < 12; i++)
  1992.                 colpos[i] = colpos[0] + colwidth[MAIN_NARROW] * 8
  1993.                                       + colwidth[MAIN_WIDE]   * (i-8)
  1994.                                       + (totalspace*i)/11;
  1995.  
  1996.             /*
  1997.              *        We always ensure that there is at least two pixels
  1998.              *        between the end of the status line and the beginning
  1999.              *        of the next gadget, otherwise we get a nasty artifact
  2000.              *        where the identically-coloured white borders meet.
  2001.              */
  2002.             colwidth[MAIN_STATUS] = colpos[3] - colpos[0] +
  2003.                                     colwidth[MAIN_NARROW];
  2004.             if ((colpos[0] + colwidth[MAIN_STATUS] + 2) > colpos[4])
  2005.                 colwidth[MAIN_STATUS] = colpos[4] - colpos[0] - 2;
  2006.  
  2007.             gridtype = TWELVE_BY_ONE;
  2008.             numrows  = 1;
  2009.         } else if (statusline && width6 <= innerwidth) {
  2010.             /*
  2011.              *        Setup layout for 6 x 2 grid.
  2012.              */
  2013.             int totalspace = innerwidth - width6;
  2014.             int i;
  2015.  
  2016.             for (i = 1; i < 4; i++)
  2017.                 colpos[i] = colpos[0] + colwidth[MAIN_NARROW]*i +
  2018.                              (totalspace*i)/5;
  2019.  
  2020.             colwidth[MAIN_STATUS] = colpos[3] - colpos[0] +
  2021.                                     colwidth[MAIN_NARROW];
  2022.  
  2023.             colpos[5]    = width - bordright - margin - colwidth[MAIN_WIDE];
  2024.             colpos[4]    = colpos[5] - colwidth[MAIN_WIDE] - totalspace/5;
  2025.  
  2026.             /*
  2027.              *        If there isn't at least 2 pixels between the status line
  2028.              *        and the adjacent wide gadget, reduce the size of each
  2029.              *        wide gadget by 1 pixel (2 pixels in total) to prevent
  2030.              *        a nasty clash between the two gadget edges.
  2031.              */
  2032.             if ((colpos[0] + colwidth[MAIN_STATUS] + 2) > colpos[4]) {
  2033.                 colpos[4] += 2;
  2034.                 colpos[5]++;
  2035.                 colwidth[MAIN_WIDE]--;
  2036.             }
  2037.             gridtype    = SIX_BY_TWO;
  2038.             numrows      = 2;
  2039.  
  2040.         } else if (!statusline && width8 <= innerwidth) {
  2041.             /*
  2042.              *        Setup layout for 8 x 1 grid
  2043.              */
  2044.             int totalspace = innerwidth - width8;
  2045.             int i;
  2046.  
  2047.             for (i = 1; i < 4; i++)
  2048.                 colpos[i] = colpos[0] + colwidth[MAIN_NARROW]*i +
  2049.                             (totalspace*i)/7;
  2050.  
  2051.             for (i = 4; i < 8; i++)
  2052.                 colpos[i] = colpos[0] + colwidth[MAIN_NARROW] * 4
  2053.                                       + colwidth[MAIN_WIDE]   * (i-4)
  2054.                                       + (totalspace*i)/7;
  2055.             gridtype = EIGHT_BY_ONE;
  2056.             numrows     = 1;
  2057.  
  2058.         } else {
  2059.             /*
  2060.              *        Setup layout for 4 x 3 grid
  2061.              */
  2062.             int totalspace = (innerwidth - width4);
  2063.  
  2064.             colwidth[MAIN_STATUS] = width - bordright - margin - colpos[0];
  2065.             colpos[3]    = width - bordright - margin - colwidth[MAIN_WIDE];
  2066.             colpos[1]    = colpos[0] + colwidth[MAIN_NARROW] + totalspace/3;
  2067.             colpos[2]    = colpos[3] - colwidth[MAIN_WIDE]   - totalspace/3;
  2068.             gridtype    = FOUR_BY_THREE;
  2069.             if (statusline)
  2070.                 numrows     = 3;
  2071.             else
  2072.                 numrows  = 2;
  2073.         }
  2074.     } else {
  2075.         /*
  2076.          *        No gadget line, so set all the gadget positions to a
  2077.          *        negative value to ensure the gadgets stay hidden, and
  2078.          *        create or don't create a status line as appropriate.
  2079.          */
  2080.         int i;
  2081.  
  2082.         colwidth[MAIN_STATUS] = innerwidth;
  2083.         gridtype              = TWELVE_BY_ONE;
  2084.         numrows               = 1;
  2085.         for (i = 1; i < 12; i++)
  2086.             colpos[i] = INVIS_LEFT_EDGE;    /* Gadget off left-hand edge */
  2087.  
  2088.         if (!statusline) {
  2089.             numrows   = 0;
  2090.             colpos[0] = INVIS_LEFT_EDGE;
  2091.         }
  2092.     }
  2093.     colwidth[MAIN_NARROW_TOGGLE] = colwidth[MAIN_NARROW];
  2094.     colwidth[MAIN_WIDE_INVIS]     = colwidth[MAIN_WIDE];
  2095.  
  2096.     /*
  2097.      *        Next, calculate how many lines we can fit in the main buffer
  2098.      *        display. The total vertical margin is fonty + 8 + BarHeight
  2099.      *        pixels, which is distributed as follows:
  2100.      *
  2101.      *                    ----titlebar----
  2102.      *                  <1 pixel border>
  2103.      *                    4 pixel gap
  2104.      *                    Column headings
  2105.      *                    <VBorderGap>
  2106.      *                    2 pixels of border
  2107.      *                    <VBorderGap>
  2108.      *                    Lines of text
  2109.      *                    <VBorderGap>
  2110.      *                    (1 pixel border)
  2111.      *
  2112.      *        And in addition, if we have gadgets enabled:
  2113.      *
  2114.      *                    (1 pixel border)
  2115.      *                    <gadg_gap>
  2116.      *                    ---gadget area---
  2117.      *                    <gadg_gap>
  2118.      *                    (1 pixel border)
  2119.      *                    ---bottom border---
  2120.      *
  2121.      *        The minimum spacing between gadgets is also gadg_gap.
  2122.      *        Each individual gadget has a height 6 pixels higher than
  2123.      *        the font size.
  2124.      *
  2125.      *        The catch is to take the <any additional space> and distribute
  2126.      *        it equally between the various gadget spaces etc. For N rows
  2127.      *        of gadgets, this implies that there are (N+1) "gaps" that can
  2128.      *        have vertical space added to them.
  2129.      */
  2130.     BoxLeft            = BorderLeft;
  2131.     BoxWidth        = width - bordright - BoxLeft;
  2132.     BoxInLeft        = BoxLeft + BOX_LEFT_MARGIN + 2;
  2133.     BoxInWidth        = bfontx * BoxCols;
  2134.     BoxCharWidth    = bfontx;
  2135.     BoxCharHeight    = bfonty;
  2136.  
  2137.     yoffset            = height - bordbot - spacing * numrows - gadg_gap;
  2138.     BoxSpacing      = bfonty + BoxInterGap;
  2139.     if (SquareAspect) {
  2140.         BoxTop            = TitlebarHeight + 6 + bfonty;
  2141.         BoxHeaderLine     = TitlebarHeight + 4 + bfontbaseline;
  2142.     } else {
  2143.         BoxTop            = TitlebarHeight + 5 + bfonty;
  2144.         BoxHeaderLine     = TitlebarHeight + 3 + bfontbaseline;
  2145.     }
  2146.     if (numrows)
  2147.         boxheight   = yoffset - gadg_gap - VBorderGap*3 - BoxTop-2;
  2148.     else
  2149.         boxheight    = height - bordbot - BoxTop - 2*VBorderGap - 2;
  2150.     BoxRows    = (boxheight + BoxInterGap) / BoxSpacing;
  2151.     if (numrows)
  2152.         BoxHeight    = BoxRows * BoxSpacing - BoxInterGap + 2*VBorderGap + 2;
  2153.     else
  2154.         BoxHeight    = height - bordbot - BoxTop;
  2155.  
  2156.     BoxLowest    = BoxTop   + BoxHeight + VBorderGap;
  2157.     BoxInTop    = BoxTop   + VBorderGap + 1;
  2158.     BoxBaseline    = BoxInTop + bfontbaseline;
  2159.     BoxInHeight    = BoxRows * BoxSpacing - BoxInterGap;
  2160.  
  2161.     InitMainMargins();
  2162.  
  2163.     /*
  2164.      *        Now get ready to create the button and status line gadgets
  2165.      *        We always create the gadgets, even if they're not currently
  2166.      *        being displayed.
  2167.      */    
  2168.     ng.ng_TextAttr        = gadgetfa;
  2169.     ng.ng_VisualInfo    = MainVI;
  2170.     ng.ng_Flags         = PLACETEXT_IN;
  2171.  
  2172.     ng.ng_GadgetText    = "";
  2173.     ng.ng_GadgetID        = 0;
  2174.  
  2175.     gadlist = NULL;
  2176.     gad = CreateContext(&gadlist);
  2177.     if (!gad)
  2178.         goto mgad_failed;
  2179.  
  2180.     /*
  2181.      *        Now, the buttons and status line gadget. At this point,
  2182.      *        we distribute any additional space that we have left between
  2183.      *        the lower scroll bar and the current start of the gadget
  2184.      *        grid (aka yoffset).
  2185.      */
  2186.     extraspace        = yoffset - (BoxTop + BoxHeight);
  2187.     yoffset           -= extraspace - extraspace/(numrows+1) -
  2188.                       ((extraspace % (numrows+1)) + 1)/2;
  2189.     spacing           += extraspace / (numrows + 1);
  2190.     yoffset           += gadg_gap;
  2191.     ng.ng_Height    = gheight; 
  2192.  
  2193.     if (gridtype == FOUR_BY_THREE && !statusline)
  2194.         yoffset -= spacing;    /* Bias to allow for invisible status bar */
  2195.  
  2196.     for (mg = &MainGadgs[0]; mg->gadgid; mg++) {
  2197.         switch (gridtype) {
  2198.             case FOUR_BY_THREE:
  2199.                 ng.ng_LeftEdge     = colpos[mg->col4];
  2200.                 ng.ng_TopEdge      = yoffset + spacing * mg->row4;
  2201.                 ng.ng_Width        = colwidth[mg->widthtype];
  2202.                 break;
  2203.  
  2204.             case SIX_BY_TWO:
  2205.                 ng.ng_LeftEdge     = colpos[mg->col6];
  2206.                 ng.ng_TopEdge      = yoffset + spacing * mg->row6;
  2207.                 ng.ng_Width        = colwidth[mg->widthtype];
  2208.                 break;
  2209.  
  2210.             case EIGHT_BY_ONE:
  2211.                 ng.ng_LeftEdge     = colpos[mg->col8];
  2212.                 ng.ng_TopEdge      = yoffset;
  2213.                 ng.ng_Width        = colwidth[mg->widthtype];
  2214.                 break;
  2215.         
  2216.             case TWELVE_BY_ONE:
  2217.                 ng.ng_LeftEdge     = colpos[mg->col12];
  2218.                 ng.ng_TopEdge      = yoffset;
  2219.                 ng.ng_Width        = colwidth[mg->widthtype];
  2220.                 break;
  2221.         }
  2222.         ng.ng_GadgetText    = MSG(mg->stringid);
  2223.         ng.ng_GadgetID        = mg->gadgid;
  2224.  
  2225.         if (mg->gadgid == GID_STATUS) {
  2226.             if (statusline) {
  2227.                 int len = GetTextLen(font, MSG(mg->stringid)) + 9;
  2228.  
  2229.                 ng.ng_LeftEdge += len;
  2230.                 ng.ng_Width       -= len;
  2231.                 ng.ng_Flags        = PLACETEXT_LEFT;
  2232.                 gad = CreateGadget(TEXT_KIND, gad, &ng,
  2233.                                    GT_Underscore,    '_',
  2234.                                    GTTX_Text,        StatusLineText,
  2235.                                    GTTX_Border,        TRUE,
  2236.                                    TAG_DONE);
  2237.                 ng.ng_Flags           = PLACETEXT_IN;
  2238.                 if (!gad)
  2239.                     goto mgad_failed;
  2240.                 Gadget[GID_STATUS] = gad;
  2241.             } else {
  2242.                 Gadget[GID_STATUS] = NULL;
  2243.             }
  2244.         } else {
  2245.             gad = CreateGadget(BUTTON_KIND, gad, &ng,
  2246.                                GT_Underscore,    '_',
  2247.                                TAG_DONE);
  2248.             if (!gad)
  2249.                 goto mgad_failed;
  2250.  
  2251.             if (mg->widthtype == MAIN_NARROW_TOGGLE)
  2252.                 gad->Activation |= GACT_TOGGLESELECT;
  2253.  
  2254.             if (mg->widthtype == MAIN_WIDE_INVIS)
  2255.                 gad->LeftEdge = INVIS_LEFT_EDGE;
  2256.  
  2257.             Gadget[mg->gadgid] = gad;
  2258.         }
  2259.         AddKeyShortcut(MainKeyboard, mg->gadgid, mg->stringid);
  2260.     }
  2261.     LogButtonLeft = Gadget[GID_OPENLOG]->LeftEdge;
  2262.     SetLogGadget(DefaultLogMode, LG_NOREFRESH);
  2263.  
  2264.     /*
  2265.      *        Now some additional initialisation for the Pause
  2266.      *        and Disable gadgets in case they're initially set
  2267.      */
  2268.     if (Paused)        Gadget[GID_PAUSE  ]->Flags |= GFLG_SELECTED;
  2269.     if (Disabled)    Gadget[GID_DISABLE]->Flags |= GFLG_SELECTED;
  2270.         
  2271.     /*
  2272.      *        Now a quick check to see if we have opened commodities.library
  2273.      *        yet -- if we haven't, then disable the hide gadget.
  2274.      */
  2275.     if (CurSettings.Setup.HideMethod == HIDE_NONE)
  2276.         Gadget[GID_HIDE]->Flags |= GFLG_DISABLED;
  2277.  
  2278.     return (gadlist);
  2279.  
  2280. mgad_failed:
  2281.     FreeMainGadgets();
  2282.     CloseFont(font);
  2283.     return (NULL);
  2284. }
  2285.  
  2286. /*
  2287.  *        RecalcMainWindow(width, height, refresh)
  2288.  *
  2289.  *        Updates the gadgets for the main window to reflect the new size,
  2290.  *        and renders the changes to the screen. If the refresh parameter
  2291.  *        is REDRAW_GADGETS, then the function will automatically redraw
  2292.  *        the gadgets in the window, otherwise this must be done by hand
  2293.  *        (or perhaps automatically if Intuition generates an IDCMP_REFRESH
  2294.  *        message after a resize event!)
  2295.  *
  2296.  *        Returns FALSE if something went wrong (in which case the caller
  2297.  *        is responsible for closing the main window) and displaying an
  2298.  *        error message.
  2299.  */
  2300. BOOL RecalcMainWindow(int width, int height, int dorefresh)
  2301. {
  2302.     struct RastPort *rport = MainWindow->RPort;
  2303.     int saveleft    = BoxLeft;
  2304.     int savewidth    = BoxWidth;
  2305.     int saveheight    = BoxHeight;
  2306.     int oldrows     = BoxRows;
  2307.     int startrow;
  2308.  
  2309.     if (!MainWindow)
  2310.         return (TRUE);
  2311.  
  2312.     if (MainGadList && !RemovedGadgets)
  2313.         RemoveGList(MainWindow, MainGadList, -1);
  2314.  
  2315.     MainGadList = CreateMainGadgets(CurMainGadgetFA, CurMainBufferFA,
  2316.                                        width, height, StatusLine);
  2317.     if (!MainGadList)
  2318.         return (FALSE);
  2319.  
  2320.     UpdateMainHScroll();
  2321.     UpdateMainVScroll();
  2322.     if (BoxWidth == savewidth && BoxLeft == saveleft && !ChangedSpacing) {
  2323.         if (BoxHeight == saveheight)
  2324.             /*
  2325.              *        Only erase from bottom of scroll box down since the
  2326.              *        rest hasn't actually changed at all.
  2327.              */
  2328.             startrow = BoxLowest;
  2329.         else {
  2330.             startrow = BoxTop + MIN(BoxHeight, saveheight) - 1;
  2331.             startrow = MAX(startrow, TitlebarHeight);
  2332.         }
  2333.     } else
  2334.         startrow = TitlebarHeight;
  2335.  
  2336.     SetDrMd(rport, JAM1);
  2337.     SetAPen(rport, 0);
  2338.     AddGList(MainWindow, MainGadList, -1, -1, NULL);
  2339.  
  2340.     if (startrow == BoxLowest) {
  2341.         /*
  2342.          *        If we are only refeshing the bottom part of the screen,
  2343.          *        don't bother erasing the top half -- it looks much
  2344.          *        cleaner.
  2345.          */
  2346.         if (StatusLine || GadgetsLine) {
  2347.             // SetAPen(rport, 4);    /* Makes gadget bground gray (nice!) */
  2348.             RectFill(rport, BoxLeft + 2, BoxTop + BoxHeight + 1,
  2349.                             BoxLeft + BoxWidth - 3,
  2350.                             MainWindow->Height - MainWindow->BorderBottom - 1);
  2351.             DrawBevelBox(rport, BoxLeft, BoxTop + BoxHeight, BoxWidth,
  2352.                                 MainWindow->Height - MainWindow->BorderBottom -
  2353.                                                      BoxTop - BoxHeight,
  2354.                                 GT_VisualInfo,    MainVI,
  2355.                                 TAG_DONE);
  2356.         }
  2357.         if (dorefresh)
  2358.             RefreshGList(MainGadList, MainWindow, NULL, -1);
  2359.     } else {
  2360.         startrow = MAX(TitlebarHeight, startrow - BoxSpacing - 3);
  2361.         RectFill(rport, MainWindow->BorderLeft, startrow,
  2362.                  width  - MainWindow->BorderRight  - 1,
  2363.                  height - MainWindow->BorderBottom - 1);
  2364.         if (dorefresh)
  2365.             RefreshGList(MainGadList, MainWindow, NULL, -1);
  2366.         /*
  2367.          *        If we are currently at the end of the display, we want to
  2368.          *        ensure that we scroll to the end after refreshing the window.
  2369.          */
  2370.         if (TopSeq + oldrows > EndSeq) {
  2371.             /*
  2372.              *        Force redraw from buffer end after resize
  2373.              */
  2374.             TopSeq   = EndSeq;
  2375.             TopEvent = EndEvent;
  2376.         }
  2377.         RedrawMainWindow();
  2378.         UpdateMainVScroll();
  2379.     }
  2380.     GT_RefreshWindow(MainWindow, NULL);
  2381.     CurWindowWidth  = MainWindow->Width;
  2382.     CurWindowHeight = MainWindow->Height;
  2383.     ChangedSpacing  = 0;
  2384.     return (TRUE);
  2385. }
  2386.  
  2387. /*
  2388.  *        SetMenuOptions()
  2389.  *
  2390.  *        Configures the main window's menu strip to match the currently
  2391.  *        selected options (e.g. Pause/Disable, Text Spacing, window type,
  2392.  *        icon type, etc.)
  2393.  *
  2394.  *        Call whenever settings change.
  2395.  *
  2396.  */
  2397. void SetMenuOptions(void)
  2398. {
  2399.     struct MenuItem *item;
  2400.  
  2401. #define SetMenuState(menunum, val) \
  2402.     item = ItemAddress(MainWinMenu, menunum); \
  2403.     item->Flags = (item->Flags & ~CHECKED) | ((val) ? CHECKED : 0)
  2404.  
  2405.     if (MainWindow && MainWinMenu) {
  2406.         int curpri = SysBase->ThisTask->tc_Node.ln_Pri;
  2407.         int i;
  2408.  
  2409.         SetMenuState(MENU_PAUSE,      Paused);
  2410.         SetMenuState(MENU_DISABLE,      Disabled);
  2411.         SetMenuState(MENU_AUTO_OPEN,  AutoOpen);
  2412.         SetMenuState(MENU_DIS_HIDDEN, DisableOnHide);
  2413.         SetMenuState(MENU_STATUS,      StatusLine);
  2414.         SetMenuState(MENU_GADGETS,      GadgetsLine);
  2415.         SetMenuState(MENU_ICONS,      CreateIcons);
  2416.         SetMenuState(MENU_SIMPLE,      CurSettings.SimpleRefresh);
  2417.         SetMenuState(MENU_SMART,      !CurSettings.SimpleRefresh);
  2418.         SetMenuState(MENU_AL_LEFT,      !RightAligned);
  2419.         SetMenuState(MENU_AL_RIGHT,      RightAligned);
  2420.         SetMenuState(MENU_SPACING0,      BoxInterGap == 0);
  2421.         SetMenuState(MENU_SPACING1,      BoxInterGap == 1);
  2422.         SetMenuState(MENU_SPACING2,      BoxInterGap == 2);
  2423.  
  2424.         SetMenuState(MENU_ROWQUAL_ANY,      RowQual == ROWQUAL_ANY);
  2425.         SetMenuState(MENU_ROWQUAL_NONE,      RowQual == ROWQUAL_NONE);
  2426.         SetMenuState(MENU_ROWQUAL_SHIFT,  RowQual == ROWQUAL_SHIFT);
  2427.         SetMenuState(MENU_ROWQUAL_ALT,      RowQual == ROWQUAL_ALT);
  2428.         SetMenuState(MENU_ROWQUAL_CTRL,      RowQual == ROWQUAL_CTRL);
  2429.         SetMenuState(MENU_ROWQUAL_ALL,      RowQual == ROWQUAL_ALL);
  2430.  
  2431.         if (CurSettings.Setup.HideMethod == HIDE_NONE)
  2432.             OffMenu(MainWindow, MENU_HIDE);
  2433.         else
  2434.             OnMenu(MainWindow,    MENU_HIDE);
  2435.  
  2436.         if (LogActive) {
  2437.             OffMenu(MainWindow,    MENU_OPENLOG);
  2438.             OnMenu(MainWindow,    MENU_CLOSELOG);
  2439.         } else {
  2440.             OnMenu(MainWindow,    MENU_OPENLOG);
  2441.             OffMenu(MainWindow,    MENU_CLOSELOG);
  2442.         }
  2443.  
  2444.         /*
  2445.          *        We calculate our current priority on the fly. This
  2446.          *        means we'll always be up-to-date, even if someone
  2447.          *        changes our priority from outside the program.
  2448.          */
  2449.         for (i = 0; i < MENU_NUMPRI; i++) {
  2450.             int subnum = MENU_CHANGEPRI | SHIFTSUB(i);
  2451.             struct MenuItem  *item  = ItemAddress(MainWinMenu, subnum);
  2452.             struct IntuiText *itext = (void *)(item->ItemFill);
  2453.  
  2454.             if (atoi(itext->IText) == curpri)
  2455.                 item->Flags |= CHECKED;
  2456.             else
  2457.                 item->Flags &= ~CHECKED;
  2458.         }
  2459.     }
  2460. }
  2461.         
  2462. /*
  2463.  *        SetMainHideState(state)
  2464.  *
  2465.  *        Updates the current main window titlebar, hide gadget, and
  2466.  *        menu HIDE option to reflect the given hide state.
  2467.  *
  2468.  *        It works as follows. If state is HIDE_NONE, then the titlebar
  2469.  *        is set to <none> and the gadget/menu item are ghosted. If
  2470.  *        state is not HIDE_NONE, the gadget/menu item are unghosted,
  2471.  *        and the titlebar is set to <key sequence>, unless HotKeyActive
  2472.  *        is set to 0, in which case it is set to <invalid>.
  2473.  */
  2474. void SetMainHideState(int hidestate)
  2475. {
  2476.     char title[200];
  2477.     char *keyname = CurSettings.Setup.HotKey;
  2478.  
  2479.     if (!HotKeyActive)
  2480.         keyname = MSG(MSG_INVALID_HOTKEY);
  2481.     
  2482.     if (hidestate == HIDE_NONE)
  2483.         strcpy(title, SnoopDosTitle);
  2484.     else
  2485.         mysprintf(title, SnoopDosTitleKey, keyname);
  2486.  
  2487.     if (strcmp(CurrentTitle, title) != 0)
  2488.         strcpy(CurrentTitle, title);
  2489.     
  2490.     if (MainWindow) {
  2491.         struct Gadget *gad = Gadget[GID_HIDE];
  2492.         int oldstate       = gad->Flags & GFLG_DISABLED;
  2493.         int gadpos;
  2494.  
  2495.         SetWindowTitles(MainWindow, CurrentTitle, (UBYTE *)-1);
  2496.  
  2497.         gadpos = RemoveGadget(MainWindow, gad);
  2498.         if (hidestate == HIDE_NONE)
  2499.             gad->Flags |= GFLG_DISABLED;
  2500.         else
  2501.             gad->Flags &= ~GFLG_DISABLED;
  2502.  
  2503.         AddGadget(MainWindow, gad, gadpos);
  2504.         if ((gad->Flags & GFLG_DISABLED) != oldstate) {
  2505.             /*
  2506.              *        Only refresh the gadget if its state changed
  2507.              */
  2508.             RefreshGList(gad, MainWindow, NULL, 1);
  2509.         }
  2510.         if (MainWinMenu) {
  2511.             if (CurSettings.Setup.HideMethod == HIDE_NONE)
  2512.                 OffMenu(MainWindow, MENU_HIDE);
  2513.             else
  2514.                 OnMenu(MainWindow,    MENU_HIDE);
  2515.         }
  2516.     }
  2517. }
  2518.  
  2519. /*
  2520.  *        OpenMainWindow()
  2521.  *
  2522.  *        Opens the main window with the scroll buffer display, arrow gadgets,
  2523.  *        and buttons. Also creates the status line gadget if necessary.
  2524.  *
  2525.  *        Returns true for success, false for failure.
  2526.  */
  2527. BOOL OpenMainWindow(void)
  2528. {
  2529.     static WORD initzoomdims[] = { 0, 0, -1, -1 };
  2530.     struct Window *win;
  2531.     struct Gadget *scrollgadlist;
  2532.     struct TextAttr *gadgetfa;
  2533.     struct TextAttr *bufferfa;
  2534.     int minx, miny;
  2535.     int menupen;
  2536.     int i;
  2537.     int width  = CurSettings.MainWinWidth;
  2538.     int height = CurSettings.MainWinHeight;
  2539.     int left   = CurSettings.MainWinLeft;
  2540.     int top    = CurSettings.MainWinTop;
  2541.  
  2542.     CheckSegTracker();
  2543.     if (MainWindow) {
  2544.         WindowToFront(MainWindow);
  2545.         ActivateWindow(MainWindow);
  2546.         return (TRUE);
  2547.     }
  2548.     if (!CheckForScreen())
  2549.         goto open_main_failed;
  2550.  
  2551.     for (i = 0; gadgetfa = MainWindowFontList[i].gadgetfa; i++) {
  2552.         bufferfa = MainWindowFontList[i].bufferfa;
  2553.  
  2554.         if (CalcMinMainSize(gadgetfa, bufferfa, &minx, &miny))
  2555.             break;
  2556.     }
  2557.     if (!gadgetfa)
  2558.         goto open_main_failed;
  2559.  
  2560.     if (width  == -1)                    width  = DEF_WINDOW_WIDTH;
  2561.     if (height == -1)                    height = DEF_WINDOW_HEIGHT;
  2562.     if (width  == 0)                    width  = CurSettings.MainWinWidth;
  2563.     if (height == 0)                    height = CurSettings.MainWinHeight;
  2564.     if (width  < minx)                     width  = minx;
  2565.     if (height < miny)                    height = miny;
  2566.     if (width  > ScreenWidth)            width  = ScreenWidth;
  2567.     if (height > ScreenHeight)            height = ScreenHeight;
  2568.     if (left   == -1)                    left   = 0;
  2569.     if (top    == -1)                    top    = SnoopScreen->BarHeight + 2;
  2570.  
  2571.     initzoomdims[2] = ScreenWidth;
  2572.     initzoomdims[3] = ScreenHeight;
  2573.         
  2574.     ShowBuffer(TopSeq, DISPLAY_NONE);    /* Ensure scroll vars are up to date */
  2575.  
  2576.     MainGadList   = CreateMainGadgets(gadgetfa, bufferfa,
  2577.                                      width, height, StatusLine);
  2578.     scrollgadlist = CreateScrollGadgets();
  2579.     if (!scrollgadlist || !MainGadList)
  2580.         goto open_main_failed;
  2581.  
  2582.     Gadget[GID_ENDSCROLL]->NextGadget = MainGadList;
  2583.  
  2584.     CurMainGadgetFA = gadgetfa;
  2585.     CurMainBufferFA = bufferfa;
  2586.  
  2587.     SetMainHideState(CurSettings.Setup.HideMethod);    /* Make title up to date */
  2588.     win = OpenWindowTags(NULL,
  2589.                          WA_Title,            CurrentTitle,
  2590.                          WA_IDCMP,            IDCMP_CLOSEWINDOW     |
  2591.                                              IDCMP_REFRESHWINDOW     |
  2592.                                             IDCMP_NEWSIZE         |
  2593.                                             IDCMP_CHANGEWINDOW     |
  2594.                                             IDCMP_RAWKEY         |
  2595.                                             IDCMP_MENUPICK         |
  2596.                                             IDCMP_IDCMPUPDATE     |
  2597.                                             IDCMP_GADGETDOWN     |
  2598.                                             IDCMP_MOUSEBUTTONS     |
  2599.                                             IDCMP_MOUSEMOVE         |
  2600.                                             IDCMP_MENUHELP          |
  2601.                                             IDCMP_SIZEVERIFY     |
  2602.                                             IDCMP_INACTIVEWINDOW |
  2603.                                             BUTTONIDCMP,
  2604.                          WA_Left,              left,
  2605.                          WA_Top,              top,
  2606.                          WA_Width,            width,
  2607.                          WA_Height,            height,
  2608.                          WA_Flags,            WFLG_CLOSEGADGET     |
  2609.                                              WFLG_DRAGBAR         |
  2610.                                             WFLG_DEPTHGADGET     |
  2611.                                             WFLG_ACTIVATE         |
  2612.                                             WFLG_SIZEGADGET         |
  2613.                                             WFLG_SIZEBBOTTOM     |
  2614.                                             WFLG_SIZEBRIGHT         |
  2615.                                             WFLG_NEWLOOKMENUS,
  2616.                          WA_MenuHelp,        TRUE,
  2617.                          RefreshTag,          TRUE,
  2618.                          WA_NoCareRefresh,    NoCareRefreshBool,
  2619.                          WA_PubScreen,        SnoopScreen,
  2620.                          WA_MinWidth,        minx,
  2621.                          WA_MinHeight,        miny,
  2622.                          WA_MaxWidth,        -1,
  2623.                          WA_MaxHeight,        -1,
  2624.                          WA_Zoom,            initzoomdims,
  2625.                          WA_Gadgets,        scrollgadlist,
  2626.                          TAG_DONE);
  2627.     if (!win)
  2628.         goto open_main_failed;
  2629.  
  2630.     MainWindow       = win;
  2631.     MainWindowPort = win->UserPort;
  2632.     MainWindowMask = 1 << MainWindowPort->mp_SigBit;
  2633.  
  2634.     if (DraggingColumn || DraggingRow) {
  2635.         /*
  2636.          *        If we were previously closed while a column was being dragged,
  2637.          *        reset the drag flag and signal ourselves to ensure we catch
  2638.          *        any events that arrived while output was suspended.
  2639.          */
  2640.         DraggingColumn = 0;
  2641.         DraggingRow    = 0;
  2642.         Signal(SysBase->ThisTask, NewEventMask);
  2643.     }
  2644.     RedrawMainWindow();
  2645.     // RefreshGList(MainGadList, MainWindow, NULL, -1);    /* Already done */
  2646.     GT_RefreshWindow(MainWindow, NULL);
  2647.     UpdateStatus();
  2648.  
  2649.     /*
  2650.      *        Now create menus for window
  2651.      */
  2652.     if (IntuitionBase->LibNode.lib_Version >= 39)
  2653.         menupen = 1;
  2654.     else
  2655.         menupen = 0;
  2656.  
  2657.     MainWinMenu = CreateMenus(MainMenu, GTMN_FrontPen, menupen, TAG_DONE);
  2658.  
  2659.     if (!MainWinMenu)
  2660.         ShowError(MSG(MSG_ERROR_CREATEMENUS));
  2661.     else {
  2662.         LayoutMenus(MainWinMenu, MainVI, GTMN_NewLookMenus, TRUE, TAG_DONE);
  2663.         SetMenuStrip(MainWindow, MainWinMenu);
  2664.         SetMenuOptions();
  2665.     }
  2666.     CurWindowWidth  = MainWindow->Width;
  2667.     CurWindowHeight = MainWindow->Height;
  2668.     AwaitingResize  = RESIZE_DONE;
  2669.     if ((SnoopScreen->Flags & SCREENTYPE) != WBENCHSCREEN) {
  2670.         /*
  2671.          *        We're running on a custom screen, so redirect system
  2672.          *        requesters to appear on this screen instead of the
  2673.          *        Workbench screen. We don't do it unconditionally, because
  2674.          *        our window title is so long that it makes the requesters
  2675.          *        look very lopsided -- still, if we're on a custom screen,
  2676.          *        it's better than nothing.
  2677.          */
  2678.         *TaskWindowPtr = MainWindow;
  2679.     }
  2680.     if (DisableNestCount)
  2681.         DisableWindow(MainWindow, &MainRequester);
  2682.  
  2683.     return (TRUE);
  2684.  
  2685. open_main_failed:
  2686.     ShowError(MSG(MSG_ERROR_OPENMAIN));
  2687.     return (FALSE);
  2688. }
  2689.  
  2690. /*
  2691.  *        CloseMainWindow(void)
  2692.  *
  2693.  *        Closes the main SnoopDos window
  2694.  */
  2695. void CloseMainWindow(void)
  2696. {
  2697.     *TaskWindowPtr = SaveWindowPtr;        /* Restore previous screen address */
  2698.     if (MainWindow) {
  2699.         if (MainWinMenu) {
  2700.             ClearMenuStrip(MainWindow);
  2701.             FreeMenus(MainWinMenu);
  2702.             MainWinMenu = NULL;
  2703.         }
  2704.         RecordWindowSizes();
  2705.         CloseWindow(MainWindow);
  2706.         FreeMainGadgets();
  2707.         MainWindow        = NULL;
  2708.         MainWindowMask    = 0;
  2709.     }
  2710.     FreeScrollGadgets();
  2711.     if (MainVI) {
  2712.         FreeVisualInfo(MainVI);
  2713.         MainVI = NULL;
  2714.     }
  2715. }
  2716.  
  2717. /*
  2718.  *        ShowStatus(msg)
  2719.  *
  2720.  *        Displays the specified message in the status line (if enabled)
  2721.  */
  2722. void ShowStatus(char *msg)
  2723. {
  2724.     strcpy(StatusLineText, msg);
  2725.  
  2726.     if (MainWindow) {
  2727.         GT_SetGadgetAttrs(Gadget[GID_STATUS], MainWindow, NULL,
  2728.                           GTTX_Text,    StatusLineText,
  2729.                           TAG_DONE);
  2730.     }
  2731. }
  2732.  
  2733. /*
  2734.  *        ReOpenMainWindow()
  2735.  *
  2736.  *        Closes and re-opens main window retaining current size but
  2737.  *        taking into account new font, spacing, refresh type, etc.
  2738.  *        If another window is specified, then that window is brought
  2739.  *        to the front after the main window is reopened.
  2740.  */
  2741. void ReOpenMainWindow(void)
  2742. {
  2743.     if (MainWindow) {
  2744.         /*
  2745.          *        If we've changed spacing but not yet updated the window to
  2746.          *        reflect this, then if we are going from 0-spacing to 2-spacing,
  2747.          *        the buffer may loose a few lines. To ensure we remain at the
  2748.          *        end of the buffer if we were there to start with, we do an
  2749.          *        invisible scroll to the _very_ end, just to be safe.
  2750.          */
  2751.         if (BottomSeq >= EndSeq) {
  2752.             TopSeq     = EndSeq;
  2753.             TopEvent = EndEvent;
  2754.         }
  2755.         CloseMainWindow();
  2756.         OpenMainWindow();
  2757.     }
  2758. }
  2759.  
  2760. /*
  2761.  *        CheckForDirtyMainWindow()
  2762.  *
  2763.  *        Checks to see if the main Window's rastport is dirty (i.e. we did
  2764.  *        a scrollraster which was partially obscured by another window, and
  2765.  *        now we need to refresh) and redraw the window if necessary.
  2766.  *
  2767.  *        Typically called after ShowBuffer()
  2768.  */
  2769. void CheckForDirtyMainWindow(void)
  2770. {
  2771.     if (MainWindow && MainWindow->RPort->Layer->Flags & LAYERREFRESH) {
  2772.         GT_BeginRefresh(MainWindow);
  2773.         RedrawMainWindow();
  2774.         GT_EndRefresh(MainWindow, TRUE);
  2775.     }
  2776. }
  2777.  
  2778. /*
  2779.  *        UpdateMainHScroll(void)
  2780.  *
  2781.  *        Updates the main horizontal scrollbar to reflect the current
  2782.  *        margin settings and buffer width.
  2783.  */
  2784. void UpdateMainHScroll(void)
  2785. {
  2786.     if (MainWindow)
  2787.         SetGadgetAttrs(Gadget[GID_HSCROLLER], MainWindow, NULL,
  2788.                        PGA_Top,        LeftCol,
  2789.                        PGA_Total,    BufferWidth,
  2790.                        PGA_Visible,    BoxCols,
  2791.                        TAG_DONE);
  2792. }
  2793.  
  2794. /*
  2795.  *        UpdateMainVScroll(void)
  2796.  *
  2797.  *        Updates the main scrollbar to reflect the current state of play.
  2798.  *        N.b. Depends on the parameters calculated during the last call
  2799.  *        to ShowBuffer.
  2800.  */
  2801. void UpdateMainVScroll(void)
  2802. {
  2803.     if (MainWindow)
  2804.         SetGadgetAttrs(Gadget[GID_VSCROLLER], MainWindow, NULL,
  2805.                        PGA_Top,        TopSeq - FirstSeq,
  2806.                        PGA_Total,    EndSeq - FirstSeq + 1,
  2807.                        PGA_Visible,    BoxRows,
  2808.                        TAG_DONE);
  2809. }
  2810.  
  2811. /*
  2812.  *        ScrollHorizontal(amount)
  2813.  *
  2814.  *        Scrolls the window horizontally to the left (-ve amount) or right
  2815.  *        (+ve amount) and does the minimum amount of refresh necessary to
  2816.  *        ensure the screen is correctly updated. i.e., we use ScrollRaster()
  2817.  *        to perform the bulk of the scroll.
  2818.  */
  2819. void ScrollHorizontal(int amount)
  2820. {
  2821.     struct RastPort *rport = MainWindow->RPort;
  2822.     int       oldpos = LeftCol;
  2823.     Event *firstevent;
  2824.     int x0, y0, x1, y1;
  2825.     int h0, h1;
  2826.     int dx;
  2827.     int dxwidth;
  2828.     int saveboxleft;
  2829.     int saveleftcol;
  2830.     int saverightcol;
  2831.     int saveboxcols;
  2832.  
  2833.     LeftCol += amount;
  2834.     InitMainMargins();
  2835.     if (!MainWindow || LeftCol == oldpos)
  2836.         return;
  2837.  
  2838.     UpdateMainHScroll();
  2839.  
  2840.     /*
  2841.      *        Okay, we're scrolling left or right, so do a ScrollRaster for
  2842.      *        part of the amount and then use ShowBuffer() to fill in the
  2843.      *        refreshed area. If there are no events or the buffer display
  2844.      *        is now out of date, do a full refresh instead.
  2845.      *
  2846.      *        We lock semaphores for the duration to ensure that the buffer
  2847.      *        doesn't change under our feet which might lead to mismatched
  2848.      *        lines of output.
  2849.      */
  2850.     LOCK_LAYERS;
  2851.     ObtainSemaphore(&BufSem);
  2852.  
  2853.     dx = LeftCol - oldpos;
  2854.     dxwidth = dx * BoxCharWidth;
  2855.  
  2856.     firstevent = HeadNode(&EventList);
  2857.     if (!firstevent || TopSeq < firstevent->seqnum || abs(dx) > (BoxCols/2)) {
  2858.         RedrawMainWindow();
  2859.         goto unlockall;
  2860.     }
  2861.  
  2862.     /*
  2863.      *        Calculate co-ordinates for scroll
  2864.      */
  2865.     h0 = BoxHeaderLine + BoxInTop - BoxBaseline;
  2866.     h1 = h0 + BoxCharHeight - 1;
  2867.     y0 = BoxInTop;
  2868.     y1 = BoxInTop + BoxInHeight - 1;
  2869.     x0 = BoxInLeft;
  2870.     x1 = BoxInLeft + (RightCol - LeftCol + 1) * BoxCharWidth - 1;
  2871.  
  2872.     if (GfxBase->LibNode.lib_Version >= 39) {
  2873.         /*
  2874.          *        Do optimised flicker-free scroll under V39
  2875.          */
  2876.         struct Hook *oldhook;
  2877.  
  2878.         oldhook = InstallLayerHook(MainWindow->WLayer, LAYERS_NOBACKFILL);
  2879.         ScrollRasterBF(rport, dxwidth, 0, x0, h0, x1, y1);
  2880.         InstallLayerHook(MainWindow->WLayer, oldhook);
  2881.     } else {
  2882.         /*
  2883.          *        Do a somewhat flickering scroll under V37 -- unfortunately,
  2884.          *        we have to do the scroll in two separate parts, to avoid
  2885.          *        erasing part of the separator bar between the header and
  2886.          *        the buffer text when it is scrolled
  2887.          */
  2888.         ScrollRaster(rport, dxwidth, 0, x0, h0, x1, h1);
  2889.         ScrollRaster(rport, dxwidth, 0, x0, y0, x1, y1);
  2890.     }
  2891.     /*
  2892.      *        Finally, refresh the window section we just vacated by
  2893.      *        temporarily changing the margins
  2894.      */
  2895.     saveboxleft  = BoxInLeft;
  2896.     saveleftcol  = LeftCol;
  2897.     saverightcol = RightCol;
  2898.     saveboxcols  = BoxCols;
  2899.  
  2900.     if (dx < 0) {
  2901.         /*
  2902.          *        Scrolling to left so refresh left side of window
  2903.          */
  2904.         if (RightCol > -dx)
  2905.             RightCol = LeftCol - dx - 1;
  2906.         BoxCols = RightCol - LeftCol + 1;
  2907.     } else {
  2908.         /*
  2909.          *        Scrolling to right so refresh right side of window
  2910.          */
  2911.         int offset = RightCol - LeftCol + 1 - dx;
  2912.         if (offset < 0)
  2913.             goto done_scroll;
  2914.  
  2915.         BoxInLeft += offset * BoxCharWidth;
  2916.         LeftCol   += offset;
  2917.         BoxCols   -= offset;
  2918.     }
  2919.     if (RightCol < LeftCol)
  2920.         goto done_scroll;
  2921.     DrawHeaderLine();
  2922.     ShowBuffer(TopSeq, DISPLAY_ALL);
  2923.  
  2924. done_scroll:
  2925.     BoxInLeft = saveboxleft;
  2926.     LeftCol   = saveleftcol;
  2927.     RightCol  = saverightcol;
  2928.     BoxCols   = saveboxcols;
  2929.     
  2930.     /*
  2931.      *        All done, now we can release semaphores and exit
  2932.      */
  2933. unlockall:
  2934.     ReleaseSemaphore(&BufSem);
  2935.     UNLOCK_LAYERS;
  2936.     CheckForDirtyMainWindow();
  2937. }
  2938.  
  2939. /*
  2940.  *        DoArrowScrolling()
  2941.  *
  2942.  *        Scrolls window in the direction of the specified arrow
  2943.  *        (GID_UPARROW, GID_DOWNARROW, GID_LEFTARROW or GID_RIGHTARROW)
  2944.  *        and by the specified amount (usually 1 but sometimes more).
  2945.  */
  2946. void DoArrowScrolling(int arrowtype, int amount)
  2947. {
  2948.     int changedvert = 0;
  2949.     int horizadjust = 0;
  2950.     int newseq;
  2951.  
  2952.     /*
  2953.      *        If we're currently dragging columns, we ignore any attempts
  2954.      *        at scrolling since it can cause problems with the format
  2955.      *        being screwed up.
  2956.      */
  2957.     if (DraggingColumn || DraggingRow)
  2958.         return;
  2959.  
  2960.     switch (arrowtype) {
  2961.         case GID_UPARROW:
  2962.             /*
  2963.              *        Move up in a message
  2964.              */
  2965.             if (TopSeq > FirstSeq) {
  2966.                 changedvert = 1;
  2967.                 newseq        = TopSeq - amount;
  2968.                 if (newseq < 1)
  2969.                     newseq = 1;
  2970.             }
  2971.             break;
  2972.  
  2973.         case GID_DOWNARROW:
  2974.             /*        We do a clever optimisation here for power users:
  2975.              *        if SnoopDos is Paused and is at the end of the
  2976.              *        buffer, then clicking on the down arrow (normally
  2977.              *        a no-op) will have the same effect as selecting
  2978.              *        single step.
  2979.              */
  2980.             if (Paused && BottomSeq >= EndSeq)
  2981.                 SingleStep();
  2982.  
  2983.             changedvert = 1;
  2984.             newseq        = TopSeq + amount;
  2985.             break;
  2986.  
  2987.         case GID_LEFTARROW:
  2988.             if (LeftCol > 0)
  2989.                 horizadjust =- amount;
  2990.             break;
  2991.  
  2992.         case GID_RIGHTARROW:
  2993.             if (LeftCol + BoxCols <= BufferWidth)
  2994.                 horizadjust = amount;
  2995.             break;
  2996.     }
  2997.     if (MainWindow) {
  2998.         if (horizadjust)
  2999.             ScrollHorizontal(horizadjust);
  3000.  
  3001.         if (changedvert) {
  3002.             InitMainMargins();
  3003.             ShowBuffer(newseq, DISPLAY_QUICK);
  3004.             UpdateMainVScroll();
  3005.             CheckForDirtyMainWindow();
  3006.         }
  3007.     }
  3008. }
  3009.  
  3010. /*
  3011.  *        InvertColumn(pos)
  3012.  *
  3013.  *        Inverts the column drawn at the specified column position, relative
  3014.  *        to the left margin of the box (i.e. 0 ... BoxCols-1)
  3015.  *
  3016.  *        mode is INVERT_FULLBOX or INVERT_HEADER, depending on whether just
  3017.  *        the top of the column or the entire column is to be inverted. This
  3018.  *        allows for optimised updates if a column's position hasn't actually
  3019.  *        changed but the header still needs to be redrawn.
  3020.  */
  3021. void InvertColumn(int col, int mode)
  3022. {
  3023.     struct RastPort *rport = &InvertRP;
  3024.     int bfontbaseline       = BufferFont->tf_Baseline;
  3025.     int y1 = BoxHeaderLine - bfontbaseline;
  3026.     int y2 = (mode == INVERT_HEADER ? (BoxTop - 1) : (BoxInTop + BoxInHeight));
  3027.  
  3028.     if (col >= 0 && col < BoxCols) {
  3029.         int x = BoxInLeft + col * BoxCharWidth + BoxCharWidth / 2;
  3030.  
  3031.         RectFill(rport, x, y1, x+1, y2);
  3032.     }
  3033. }
  3034.  
  3035. /*
  3036.  *        StartDragCol(x)
  3037.  *
  3038.  *        Starts dragging a column displayed in the main window at absolute
  3039.  *        coordinate X.
  3040.  *
  3041.  *        Initialises all the variables associated with this and returns
  3042.  *        with dragging enabled.
  3043.  */
  3044. void StartDragCol(int x)
  3045. {
  3046.     EventFormat *ef;
  3047.     int clickcol;
  3048.  
  3049.     if (BufferEFormat[0].type == EF_END)    /* Skip empty event formats */
  3050.         return;
  3051.  
  3052.     /*
  3053.      *        Now scan event buffer and figure out which heading we match
  3054.      *        Note that the user must click on the event heading itself,
  3055.      *        or it won't count (i.e. clicking on space between columns
  3056.      *        doesn't work) -- this is to prevent confusion.
  3057.      */
  3058.     clickcol = LeftCol + ((x - BoxInLeft) / BoxCharWidth);
  3059.     {
  3060.         /*
  3061.          *        Select the column to drag
  3062.          */
  3063.         int colpos = 0;
  3064.         int len    = 0;
  3065.  
  3066.         for (ef = BufferEFormat; ef->type != EF_END; ef++) {
  3067.             if (clickcol >= colpos && clickcol < (colpos + ef->width + 1))
  3068.                 break;
  3069.             
  3070.             colpos += ef->width + 1;
  3071.         }
  3072.         /*
  3073.          *        Now check if the clickcol should affect the left or right
  3074.          *        sides of the selected column. The leftmost column is a
  3075.          *        special case (in that we always choose the righthand column
  3076.          *        rather  the preceding lefthand, since you can't drag the
  3077.          *        lefthand column any further to the left).
  3078.          *
  3079.          *        In the normal case, if there is any blank area to the right of
  3080.          *        the column, then clicking in the blank area activates the
  3081.          *        the right column adjustment, else the left column adjustment.
  3082.          *        If there is no blank area, then clicking in the rightmost
  3083.          *        column activates the right column, else the left column.
  3084.          */
  3085.         if (ef->type != EF_END)
  3086.             len = strlen(MSG(ef->titlemsgid));
  3087.  
  3088.         if (ef == BufferEFormat || ef->type == EF_END ||
  3089.             clickcol >= (colpos + MIN(ef->width-1, len)))
  3090.         {
  3091.             /*
  3092.              *        Adjusting the righthand side of the selected column
  3093.              */
  3094.             if (ef->type == EF_END) {
  3095.                 DragEvent       = ef-1;
  3096.                 SelectEvent       = ef-1;
  3097.                 SelectStartCol = colpos - SelectEvent->width - 1;
  3098.             } else {
  3099.                 SelectEvent    = ef;
  3100.                 SelectStartCol = colpos;
  3101.                 DragEvent         = ef;
  3102.             }
  3103.             DragOrigColWidth = DragEvent->width;
  3104.             NextOrigColWidth = (ef+1)->width;
  3105.         } else {
  3106.             /*
  3107.              *        Adjusting the lefthand side of the selected column
  3108.              */
  3109.             SelectEvent      = ef;
  3110.             SelectStartCol     = colpos;
  3111.             DragEvent           = ef-1;
  3112.             DragOrigColWidth = DragEvent->width;
  3113.             NextOrigColWidth = ef->width;
  3114.         }
  3115.     }
  3116.     OrigLeftCol        = LeftCol;
  3117.     OrigBufWidth    = BufferWidth;
  3118.     ClickStartCol    = clickcol;
  3119.     ClickCurCol        = clickcol;
  3120.     DraggingColumn     = 1;
  3121.     MovedColumn        = 0;
  3122.     DrawHeaderLine();
  3123.     /*
  3124.      *        Now initialise the rastport we use for the inverted column
  3125.      *        (we use a separate rastport for speed; otherwise, we have
  3126.      *        to keep initialising it, which is rather slow.)
  3127.      */
  3128.     InvertRP = *(MainWindow->RPort);
  3129.     if (GfxBase->LibNode.lib_Version >= 39)
  3130.         SetWriteMask(&InvertRP, 1);
  3131.     else
  3132.         InvertRP.Mask = 1;
  3133.     SetAPen(&InvertRP, 1);
  3134.     SetBPen(&InvertRP, 0);
  3135.     SetDrMd(&InvertRP, COMPLEMENT);
  3136.  
  3137.     InvertColumn(SelectStartCol-LeftCol-1,                     INVERT_FULLBOX);
  3138.     InvertColumn(SelectStartCol-LeftCol+SelectEvent->width, INVERT_FULLBOX);
  3139.  
  3140.     ReportMouse(TRUE, MainWindow);
  3141.     MainWindow->IDCMPFlags |= IDCMP_INTUITICKS;
  3142.     MainWindow->Flags      |= WFLG_RMBTRAP;    /* Need to detect RMB for cancel */
  3143. }
  3144.  
  3145. /*
  3146.  *        SizeColumn(eventptr, newwidth, movetype)
  3147.  *
  3148.  *        Adjusts the width of the specified event entry to the new
  3149.  *        value given, performing any scrolling and updating of the
  3150.  *        main window that may be required.
  3151.  *
  3152.  *        movetype is MOVECOL_FREE to allow free movement between the
  3153.  *        columns to the left and right, or MOVECOL_FIXED to force
  3154.  *        the columns to the right to move along with the column being
  3155.  *        sized.
  3156.  */
  3157. void SizeColumn(EventFormat *ef, int newwidth, int movetype)
  3158. {
  3159.     EventFormat *nextef = ef+1;
  3160.     int oldleftcol         = LeftCol;
  3161.     int dwidth            = newwidth - ef->width;
  3162.     int oldbufwidth        = BufferWidth;
  3163.     int oldpos1            = SelectStartCol - LeftCol - 1;
  3164.     int oldpos2            = SelectStartCol - LeftCol + SelectEvent->width;
  3165.     int newpos1;
  3166.     int newpos2;
  3167.     int mode1;
  3168.     int mode2;
  3169.  
  3170.     if (dwidth == 0)
  3171.         return;
  3172.  
  3173.     if (movetype == MOVECOL_FREE &&  nextef->type    != EF_END     &&
  3174.                                     (nextef+1)->type != EF_END) {
  3175.         /*
  3176.          *        Bound movement so that we don't make the column to the
  3177.          *        right of the main column exceed its bounds (in either
  3178.          *        direction).
  3179.          */
  3180.         int nextwidth = nextef->width - dwidth;
  3181.  
  3182.         if (nextwidth < 1)                nextwidth = 1;
  3183.         if (nextwidth > MAX_FIELD_LEN)    nextwidth = MAX_FIELD_LEN;
  3184.  
  3185.         if (nextwidth != (nextef->width - dwidth)) {
  3186.             dwidth   = nextef->width - nextwidth;
  3187.             newwidth = ef->width + dwidth;
  3188.         }
  3189.         nextef->width = nextwidth;
  3190.     } else {
  3191.         /*
  3192.          *        Dragging all the columns to the right of this one too so
  3193.          *        update width to reflect the change
  3194.          */
  3195.         BufferWidth   += dwidth;
  3196.     }
  3197.     ef->width      = newwidth;
  3198.     if (ef < SelectEvent)
  3199.         SelectStartCol += dwidth;
  3200.     ClickCurCol   += dwidth;
  3201.     ClearMainRHS   = 1;
  3202.     InitMainMargins();
  3203.  
  3204.     /*
  3205.      *        Check if the drag mouse position is off the right edge of the
  3206.      *        window; if it is, then scroll the window to keep it in view.
  3207.      */
  3208.     if (ClickCurCol >= (LeftCol + BoxCols)) {
  3209.         LeftCol = ClickCurCol - BoxCols + 1;
  3210.         InitMainMargins();
  3211.     }
  3212.     if (BufferWidth != oldbufwidth || LeftCol != oldleftcol)
  3213.         UpdateMainHScroll();
  3214.  
  3215.     newpos1 = SelectStartCol - LeftCol - 1;
  3216.     newpos2 = SelectStartCol - LeftCol + SelectEvent->width;
  3217.  
  3218.     mode1 = mode2 = INVERT_FULLBOX;
  3219.  
  3220.     if (newpos1 == oldpos1) mode1 = INVERT_HEADER;
  3221.     if (newpos2 == oldpos2) mode2 = INVERT_HEADER;
  3222.  
  3223.     InvertColumn(oldpos1, mode1);
  3224.     InvertColumn(oldpos2, mode2);
  3225.     DrawHeaderLine();
  3226.     InvertColumn(newpos1, mode1);
  3227.     InvertColumn(newpos2, mode2);
  3228.  
  3229.     MovedColumn = 1;    /* Show we updated a column for FinishDragCol() */
  3230. }
  3231.  
  3232. /*
  3233.  *        MoveColumn(xpos, movetype)
  3234.  *
  3235.  *        Mini function used when dragging columns with the mouse --
  3236.  *        changes the width of the current column to pixel position xpos,
  3237.  *        as much as possible.
  3238.  *
  3239.  *        movetype is either MOVECOL_FREE or MOVECOL_FIXED -- see the
  3240.  *        description of SizeColumn() for more details.
  3241.  */
  3242. void MoveColumn(int xpos, int movetype)
  3243. {
  3244.     int colpos = LeftCol + ((xpos - BoxInLeft) / BoxCharWidth);
  3245.  
  3246.     if (DragEvent) {
  3247.         int disp     = colpos - ClickStartCol;
  3248.         int newwidth = DragOrigColWidth + disp;
  3249.         int dwidth   = newwidth - DragEvent->width;
  3250.  
  3251.         /*
  3252.          *        Now check to see if the change we're making will
  3253.          *        cause us to scroll off the left edge of the window.
  3254.          *        If it will, then reduce the change so that we scroll
  3255.          *        at a steady 1-character rate (this works out around
  3256.          *        10 chars a second, which should be fast enough).
  3257.          */
  3258.         if (LeftCol > 0 && RightCol > (BufferWidth + dwidth))
  3259.         {
  3260.             dwidth   = RightCol - BufferWidth;
  3261.             newwidth = DragEvent->width + dwidth;
  3262.         }
  3263.         if (newwidth < 1)                newwidth = 1;
  3264.         if (newwidth > MAX_FIELD_LEN)    newwidth = MAX_FIELD_LEN;
  3265.  
  3266.         SizeColumn(DragEvent, newwidth, movetype);
  3267.     }
  3268. }
  3269.  
  3270. /*
  3271.  *        FinishDragCol()
  3272.  *
  3273.  *        Finishes up a drag operation (just resets a few variables)
  3274.  */
  3275. void FinishDragCol(void)
  3276. {
  3277.     if (!DraggingColumn)
  3278.         return;
  3279.  
  3280.     ReportMouse(FALSE, MainWindow);
  3281.     MainWindow->IDCMPFlags &= ~IDCMP_INTUITICKS;
  3282.     MainWindow->Flags      &= ~WFLG_RMBTRAP;    /* Re-enable menus    */
  3283.     DraggingColumn = 0;
  3284.  
  3285.     /*
  3286.      *        We can optimise our update a bit by checking MovedColumn.
  3287.      *        This will only be true if the user actually resized one
  3288.      *        of the columns, so it saves us having to do a redraw
  3289.      *        if a column heading was accidentally clicked but not
  3290.      *        moved.
  3291.      */
  3292.     if (SelectEvent) {
  3293.         InvertColumn(SelectStartCol-LeftCol-1,                     INVERT_FULLBOX);
  3294.         InvertColumn(SelectStartCol-LeftCol+SelectEvent->width,INVERT_FULLBOX);
  3295.     }
  3296.     if (MovedColumn) {
  3297.         /*
  3298.          *        Now update the settings window and format window with the
  3299.          *        new format. This will also redraw the main window and
  3300.          *        header line for us.
  3301.          */
  3302.         BuildFormatString(BufferEFormat, BufFormat, MAX_FORM_LEN);
  3303.         InstallNewFormat(NEW_STRING);
  3304.     } else {
  3305.         /*
  3306.          *        Nothing else to update, so just redraw the header line
  3307.          *        (which will now be completely black again with no
  3308.          *        highlighted item.)
  3309.          */
  3310.         ClearMainRHS = 1;
  3311.         DrawHeaderLine();
  3312.     }
  3313.     /*
  3314.      *        Since we disable event scanning when columns are being dragged,
  3315.      *        we signal ourselves to check for new events in case any
  3316.      *        arrived while we were playing.
  3317.      */
  3318.     Signal(SysBase->ThisTask, NewEventMask);
  3319. }
  3320.  
  3321. /*
  3322.  *        StartDragRow(int y)
  3323.  *
  3324.  *        Starts dragging a highlight at y (pixel row) in the event output.
  3325.  *        This allows the user to use the mouse to highlight an entire line,
  3326.  *        making it easy to see all the information on the line related to
  3327.  *        the event. (This can sometimes be difficult to see in a normal
  3328.  *        window when there is a lot of white space between columns).
  3329.  *
  3330.  *        Initialises all the variables associated with this and returns
  3331.  *        with dragging enabled.
  3332.  */
  3333. void StartDragRow(int y)
  3334. {
  3335.     if (BufferEFormat[0].type == EF_END)    /* Skip empty event formats */
  3336.         return;
  3337.  
  3338.     SelectRow = 0;
  3339.     if (y > BoxInTop)
  3340.         SelectRow = (y - BoxInTop) / BoxSpacing;
  3341.  
  3342.     if (SelectRow >= BoxRows)
  3343.         SelectRow = BoxRows - 1;
  3344.  
  3345.     if ((TopSeq + SelectRow) > BottomSeq)
  3346.         SelectRow = BottomSeq - TopSeq;
  3347.  
  3348.     DraggingRow = 1;
  3349.     ReportMouse(TRUE, MainWindow);
  3350.     MainWindow->IDCMPFlags |= IDCMP_INTUITICKS;
  3351.     DrawSelectedLine(SelectRow, TRUE);
  3352. }
  3353.  
  3354. /*
  3355.  *        MoveToRow(y)
  3356.  *
  3357.  *        Moves the row highlight from its current position to a new position
  3358.  *        if necessary (doesn't render anything if the row position remains
  3359.  *        unchanged.)
  3360.  */
  3361. void MoveToRow(int y)
  3362. {
  3363.     int newrow = 0;
  3364.  
  3365.     if (y > BoxInTop)
  3366.         newrow = (y - BoxInTop) / BoxSpacing;
  3367.  
  3368.     if (newrow >= BoxRows)
  3369.         newrow = BoxRows - 1;
  3370.  
  3371.     if ((TopSeq + newrow) > BottomSeq)
  3372.         newrow = BottomSeq - TopSeq;
  3373.  
  3374.     if (newrow != SelectRow) {
  3375.         DrawSelectedLine(SelectRow, FALSE);
  3376.         DrawSelectedLine(newrow,    TRUE);
  3377.         SelectRow = newrow;
  3378.     }
  3379. }
  3380.  
  3381. /*
  3382.  *        FinishDragRow()
  3383.  *
  3384.  *        Stops highlighting the selected row and restores everything to normal
  3385.  */
  3386. void FinishDragRow(void)
  3387. {
  3388.     if (!DraggingRow)
  3389.         return;
  3390.  
  3391.     DrawSelectedLine(SelectRow, FALSE);
  3392.     ReportMouse(FALSE, MainWindow);
  3393.     MainWindow->IDCMPFlags &= ~IDCMP_INTUITICKS;
  3394.     DraggingRow = 0;
  3395.  
  3396.     /*
  3397.      *        It's possible that while we were playing with the row highlight,
  3398.      *        the entire buffer may have been swept away under our feet,
  3399.      *        leading to a slightly corrupt display (events out of sequence).
  3400.      *        We don't mind this while we're dragging the highlight, but we want
  3401.      *        to ensure it's sorted out properly afterwards. So, we do a quick
  3402.      *        check to see if the current display is no longer valid, and if so,
  3403.      *        then we redraw it. We need to handle new events first, else lots
  3404.      *        of important internal variables will be out of sync.
  3405.      */
  3406.     if (TopSeq < RealFirstSeq) {
  3407.         HandleNewEvents();
  3408.         ShowBuffer(TopSeq, DISPLAY_ALL);
  3409.     }
  3410. }
  3411.  
  3412. /*
  3413.  *        BeginResize()
  3414.  *
  3415.  *        Removes gadget list from window and does a few other things
  3416.  *        in preparation for a window resize occurring in the very
  3417.  *        near future.
  3418.  */
  3419. void BeginResize(void)
  3420. {
  3421.     if (!RemovedGadgets)
  3422.         RemoveGList(MainWindow, MainGadList, -1);
  3423.     RemovedGadgets = TRUE;
  3424.     if (BottomSeq >= EndSeq)
  3425.         AwaitingResize = RESIZE_BOTTOM;
  3426.     else
  3427.         AwaitingResize = RESIZE_MIDDLE;
  3428. }
  3429.  
  3430. /*
  3431.  *        EndResize(void)
  3432.  *
  3433.  *        Tiedies up after the window has been resized -- recalculates
  3434.  *        gadget list, refreshes display, etc.
  3435.  *
  3436.  *        Returns true for success, false for failure.
  3437.  */
  3438. int EndResize(void)
  3439. {
  3440.     if (MainWindow->Width  == CurWindowWidth &&
  3441.         MainWindow->Height == CurWindowHeight)
  3442.     {
  3443.         /*
  3444.          *        Window didn't change size after all, so don't make any
  3445.          *        changes, just restore the status quo
  3446.          */
  3447.         if (RemovedGadgets)
  3448.             AddGList(MainWindow, MainGadList, ~0, -1, NULL);
  3449.         AwaitingResize = RESIZE_DONE;
  3450.     } else {
  3451.         /*
  3452.          *        Window was resized. If necessary, reposition the buffer
  3453.          *        at the end of the new window (otherwise, if we were at
  3454.          *        the end of the buffer and the window got smaller, there
  3455.          *        would now be a few lines below the bottom of the window.)
  3456.          */
  3457.         if (AwaitingResize == RESIZE_BOTTOM)
  3458.             TopSeq = EndSeq;
  3459.         AwaitingResize = RESIZE_DONE;
  3460.         if (!RecalcMainWindow(MainWindow->Width,
  3461.                               MainWindow->Height,
  3462.                               REDRAW_GADGETS)) {
  3463.             ShowError(MSG(MSG_ERROR_RESIZE));
  3464.             return (0);
  3465.         }
  3466.         // RefreshWindowFrame(MainWindow);
  3467.     }
  3468.     RemovedGadgets = FALSE;
  3469.  
  3470.     /*
  3471.      *        Now, since we disabled monitoring of events during
  3472.      *        the resize, some may have accumulated that we missed.
  3473.      *        So we generate a fake new events signal to force a
  3474.      *        recheck in case anything did arrive.
  3475.      */
  3476.     Signal(SysBase->ThisTask, NewEventMask);
  3477.     return (1);
  3478. }
  3479.  
  3480. /*
  3481.  *        SetMainWindowWidth(int width)
  3482.  *
  3483.  *        Sets the width of the main window to the specified number of
  3484.  *        characters. Note that this call won't take effect immediately;
  3485.  *        it just tells Intuition to resize our window, which will generate
  3486.  *        a NEWSIZE event that we can trap.
  3487.  *
  3488.  *        Note: has no effect if main window isn't open.
  3489.  */
  3490. void SetMainWindowWidth(int colwidth)
  3491. {
  3492.     if (colwidth == 0)
  3493.         colwidth = BufferWidth;
  3494.  
  3495.     if (MainWindow) {
  3496.         int newleft      = MainWindow->LeftEdge;
  3497.         int newtop      = MainWindow->TopEdge;
  3498.         int newheight = MainWindow->Height;
  3499.         int newwidth  = MainWindow->Width - BoxWidth +
  3500.                         colwidth*BoxCharWidth + 2 * BOX_LEFT_MARGIN + 5;
  3501.  
  3502.         if (newwidth < MainWindow->MinWidth)
  3503.             newwidth = MainWindow->MinWidth;
  3504.         
  3505.         if ((newleft + newwidth) > ScreenWidth)
  3506.             newleft  = ScreenWidth - newwidth;
  3507.  
  3508.         if (newleft < 0) {
  3509.             newleft  = 0;
  3510.             newwidth = ScreenWidth;
  3511.         }
  3512.         /*
  3513.          *        Now check if we're so close to the edge of the screen that
  3514.          *        a single additional character space would push us over the
  3515.          *        edge .. if so, then expand the width to the full screen
  3516.          *        width, since it looks neater when the window completely
  3517.          *        fills the screen, instead of "almost" filling it.
  3518.          */
  3519.         if (newleft == 0 && (newwidth + BoxCharWidth) > ScreenWidth)
  3520.             newwidth = ScreenWidth;
  3521.  
  3522.         BeginResize();
  3523.         ChangeWindowBox(MainWindow, newleft, newtop, newwidth, newheight);
  3524.     }
  3525. }
  3526.  
  3527. /*
  3528.  *        HandleMainMsgs()
  3529.  *
  3530.  *        Processes all outstanding messages associated with the main SnoopDos
  3531.  *        window. To be called whenever we get a main window event.
  3532.  */
  3533. void HandleMainMsgs(void)
  3534. {
  3535.     static int activegad;            /* Non-zero if gadget is depressed   */
  3536.     static int activekey;            /* Keycode shortcut of activegad     */
  3537.  
  3538.     struct IntuiMessage *imsg;
  3539.     Settings *newsettings = NULL;
  3540.     int loadlastsaved = 0;
  3541.     int donemain = 0;
  3542.     int origtopseq;
  3543.     int updateslider;
  3544.     int changedhoriz;
  3545.     int changedvert;
  3546.     int amount;
  3547.     ULONG newval;
  3548.     int refreshtype     = CurSettings.SimpleRefresh;
  3549.     int newspacing      = BoxInterGap;
  3550.     int newstatus        = StatusLine;
  3551.     int newgadgets        = GadgetsLine;
  3552.     int newmode         = MonitorType;
  3553.     int newalign        = RightAligned;
  3554.     int dohide          = 0;
  3555.  
  3556.     if (!MainWindow)
  3557.         return;
  3558.  
  3559.     while ((imsg = GT_GetIMsg(MainWindowPort)) != NULL) {
  3560.         ULONG code  = imsg->Code;
  3561.         int shift   = imsg->Qualifier & IE_SHIFT;
  3562.         int altctrl = imsg->Qualifier & (IE_ALT | IE_CTRL);
  3563.         struct Gadget *gad;
  3564.  
  3565.         switch (imsg->Class) {
  3566.             
  3567.             case IDCMP_CLOSEWINDOW:
  3568.                 if (CurSettings.Setup.HideMethod == HIDE_NONE) {
  3569.                     donemain = 1;
  3570.                     QuitFlag = 1;
  3571.                 } else {
  3572.                     dohide = 1;
  3573.                 }
  3574.                 break;
  3575.  
  3576.             case IDCMP_REFRESHWINDOW:
  3577.                 /*
  3578.                  *        Since the events currently displayed in our buffer
  3579.                  *        window may have scrolled off the top of the buffer
  3580.                  *        in the meantime, if we just blindly refresh, we
  3581.                  *        might end up with the refreshed portions not matching
  3582.                  *        the rest of the display. So, first try and redraw the
  3583.                  *        buffer to ensure it contains current data. Normally,
  3584.                  *        this will end up being a NOP.
  3585.                  *
  3586.                  *        See HandleNewEvents() for details as to why we need
  3587.                  *        to lock our layers first.
  3588.                  */
  3589.                 DB("Begin Refresh Semapore\n");
  3590.                 origtopseq = LastDrawnTopSeq;
  3591.                 LOCK_LAYERS;
  3592.                 ObtainSemaphore(&BufSem);
  3593.                 if (TopSeq != origtopseq)
  3594.                     ShowBuffer(TopSeq, DISPLAY_ALL);
  3595.                 GT_BeginRefresh(MainWindow);
  3596.                 RedrawMainWindow();
  3597.                 GT_EndRefresh(MainWindow, TRUE);
  3598.                 DB("End refresh semaphore\n");
  3599.                 ReleaseSemaphore(&BufSem);
  3600.                 UNLOCK_LAYERS;
  3601.                 if (TopSeq != origtopseq)
  3602.                     UpdateMainVScroll();
  3603.                 break;
  3604.  
  3605.             case IDCMP_MENUHELP:
  3606.                 /*
  3607.                  *        Display simple help for whatever menu was
  3608.                  *        active when the HELP key was pressed. To decide
  3609.                  *        which menu item to display help on, we simply
  3610.                  *        build a description string from the menu name
  3611.                  *        itself (replacing spaces with underscores and
  3612.                  *        skipping over any dots).
  3613.                  *
  3614.                  *        We also strip out sub-menu items and just show
  3615.                  *        the help for the parent item instead.
  3616.                  */
  3617.                 if (code != MENUNULL) {
  3618.                     static int menunames[] = {
  3619.                         MSG_LINK_MENU_PROJECT,
  3620.                         MSG_LINK_MENU_WINDOW,
  3621.                         MSG_LINK_MENU_SETTINGS,
  3622.                         MSG_LINK_MENU_BUFFER
  3623.                     };
  3624.                     char msgname[50];
  3625.                     char *msg;
  3626.                     int menunum = MENUNUM(code);
  3627.                     
  3628.                     if (menunum == NOMENU)
  3629.                         msg = MSG(MSG_LINK_MAINWIN);
  3630.                     else if (ITEMNUM(code) == NOITEM) {
  3631.                         msg = MSG(menunames[menunum]);
  3632.                     } else {
  3633.                         struct MenuItem *item;
  3634.  
  3635.                         item = ItemAddress(MainWinMenu,code | SHIFTSUB(NOSUB));
  3636.                         if (!item || !(item->Flags & ITEMTEXT)) {
  3637.                             msg = MSG(menunames[menunum]);
  3638.                         } else {
  3639.                             char *p;
  3640.  
  3641.                             msg = ((struct IntuiText *)item->ItemFill)->IText;
  3642.                             strcpy(msgname, "LINK Menu_");
  3643.                             p = msgname + strlen(msgname);
  3644.  
  3645.                             while (*msg) {
  3646.                                 if (*msg == ' ' || *msg == '-')
  3647.                                     *p++ = '_';
  3648.                                 else if (*msg != '.' && *msg != '?')
  3649.                                     *p++ = *msg;
  3650.                                 msg++;
  3651.                             }
  3652.                             *p = '\0';
  3653.                             msg = msgname;
  3654.                         }
  3655.                     }
  3656.                     ShowAGuide(msg);
  3657.                 }
  3658.                 /*
  3659.                  *        It's possible the user may have played with some
  3660.                  *        menu toggles while the menu was displayed ... to
  3661.                  *        avoid us missing any items that were changed, we
  3662.                  *        simply reset them all back to the original state.
  3663.                  */        
  3664.                 SetMenuOptions();
  3665.                 break;
  3666.  
  3667.             case IDCMP_MENUPICK:
  3668.                 while (code != MENUNULL) {
  3669.                     struct MenuItem *item;
  3670.                     int checked;
  3671.                     
  3672.                     item    = ItemAddress(MainWinMenu, code);
  3673.                     checked = (item->Flags & CHECKED) == CHECKED;
  3674.  
  3675.                     switch ((ULONG)GTMENUITEM_USERDATA(item)) {
  3676.  
  3677.                         case MID_OPENLOG:
  3678.                             /*
  3679.                              *        Open a new logfile
  3680.                              */
  3681.                             if (SelectFile(ChosenLogName, ChosenLogName,
  3682.                                            MainWindow, FILESEL_NEWLOGNAME))
  3683.                             {
  3684.                                 if (!OpenLog(LOGMODE_PROMPT, ChosenLogName))
  3685.                                     ShowError(MSG(MSG_ERROR_STARTLOG),
  3686.                                               DefaultLogName);
  3687.                             }
  3688.                             break;
  3689.  
  3690.                         case MID_CLOSELOG:
  3691.                             CloseLog();
  3692.                             break;
  3693.  
  3694.                         case MID_PAUSE:
  3695.                             if (checked)
  3696.                                 newmode = MONITOR_PAUSED;
  3697.                             else
  3698.                                 newmode = MONITOR_NORMAL;
  3699.                             break;
  3700.  
  3701.                         case MID_DISABLE:
  3702.                             if (checked)
  3703.                                 newmode = MONITOR_DISABLED;
  3704.                             else
  3705.                                 newmode = MONITOR_NORMAL;
  3706.                             break;
  3707.  
  3708.                         case MID_STEP:
  3709.                             SingleStep();
  3710.                             newmode = MonitorType;
  3711.                             break;
  3712.  
  3713.                         case MID_CHANGEPRI:
  3714.                             /*
  3715.                              *        New task priority selected -- let's
  3716.                              *        read the priority directly from the
  3717.                              *        menu item itself.
  3718.                              */
  3719.                             if (checked) {
  3720.                                 struct IntuiText *itext;
  3721.                                 int newpri;
  3722.                                 
  3723.                                 itext  = (void *)item->ItemFill;
  3724.                                 newpri = atoi(itext->IText);
  3725.                                 SetTaskPri(SysBase->ThisTask, newpri);
  3726.                             }
  3727.                             break;
  3728.  
  3729.                         case MID_HELP:
  3730.                             ShowAGuide(MSG(MSG_LINK_MAIN));
  3731.                             break;
  3732.  
  3733.                         case MID_ABOUT:
  3734.                             ShowError(MSG(MSG_ABOUT_SNOOPDOS), Version+6,
  3735.                                       PORT_NAME);
  3736.                             break;
  3737.  
  3738.                         case MID_HIDE:
  3739.                             dohide = 1;
  3740.                             break;
  3741.  
  3742.                         case MID_QUIT:
  3743.                             donemain = 1;
  3744.                             QuitFlag = 1;
  3745.                             break;
  3746.  
  3747.                         /*
  3748.                          *        Windows menu
  3749.                          */
  3750.                         case MID_SETUP:        
  3751.                             if (shift && SetWindow)
  3752.                                 CloseSettingsWindow();
  3753.                             else
  3754.                                 OpenSettingsWindow();
  3755.                             break;
  3756.  
  3757.                         case MID_FUNCTION:
  3758.                             if (shift && FuncWindow)
  3759.                                 CloseFunctionWindow();
  3760.                             else
  3761.                                 OpenFunctionWindow();
  3762.                             break;
  3763.  
  3764.                         case MID_FORMAT:
  3765.                             if (shift && FormWindow)
  3766.                                 CloseFormatWindow();
  3767.                             else
  3768.                                 OpenFormatWindow();
  3769.                             break;
  3770.  
  3771.                         case MID_SETWIDTH:
  3772.                         {
  3773.                             struct IntuiText *itext = (void *)item->ItemFill;
  3774.                             int newwidth            = atoi(itext->IText);
  3775.                                 
  3776.                             SetMainWindowWidth(newwidth);
  3777.                             break;
  3778.                         }
  3779.                         case MID_ROWQUAL:
  3780.                         {
  3781.                             /*
  3782.                              *        Changing to a new row qualifier
  3783.                              */
  3784.                             int newqual = SUBNUM(code);
  3785.  
  3786.                             if (newqual != NOSUB)
  3787.                                 RowQual = newqual;
  3788.                             break;
  3789.                         }
  3790.                         case MID_STATUS:    newstatus  = checked;    break;
  3791.                         case MID_GADGETS:    newgadgets = checked;    break;
  3792.                         case MID_AUTO_OPEN:    AutoOpen   = checked;    break;
  3793.                         case MID_DISABLE_HIDDEN: DisableOnHide = checked;break;
  3794.  
  3795.                         case MID_SPACE_NONE:    if (checked) newspacing  = 0;
  3796.                                                 break;
  3797.                         case MID_SPACE_1P:        if (checked) newspacing  = 1;
  3798.                                                 break;
  3799.                         case MID_SPACE_2P:        if (checked) newspacing  = 2;
  3800.                                                 break;
  3801.                         case MID_REF_SIMPLE:    if (checked) refreshtype = 1;
  3802.                                                 break;
  3803.                         case MID_REF_SMART:        if (checked) refreshtype = 0;
  3804.                                                 break;
  3805.                         case MID_ALIGN_LEFT:    if (checked) newalign    = 0;
  3806.                                                 break;
  3807.                         case MID_ALIGN_RIGHT:    if (checked) newalign    = 1;
  3808.                                                 break;
  3809.  
  3810.                         /*
  3811.                          *        Settings menu
  3812.                          */
  3813.                         case MID_LOAD:
  3814.                             if (SelectFile(ConfigFileName, ConfigFileName,
  3815.                                            MainWindow, FILESEL_LOADCONFIG))
  3816.                             {
  3817.                                 /*
  3818.                                  *        This new settings file will becomoe
  3819.                                  *        the last saved file, so just load
  3820.                                  *        it in at the end instead
  3821.                                  */
  3822.                                 newsettings      = 0;
  3823.                                 loadlastsaved = 1;
  3824.                             }
  3825.                             break;
  3826.                             
  3827.                         case MID_SAVE:
  3828.                             if (!SaveConfig(DefaultConfigName, SAVE_NOICON))
  3829.                                 ShowError(MSG(MSG_ERROR_SAVING_SETTINGS),
  3830.                                           DefaultConfigName);
  3831.                             break;
  3832.                         
  3833.                         case MID_SAVEAS:
  3834.                             if (SelectFile(ConfigFileName, ConfigFileName,
  3835.                                            MainWindow, FILESEL_SAVECONFIG))
  3836.                             {
  3837.                                 if (!SaveConfig(ConfigFileName, SAVE_ICON)) {
  3838.                                     ShowError(MSG(MSG_ERROR_SAVING_SETTINGS),
  3839.                                               ConfigFileName);
  3840.                                 }
  3841.                             }
  3842.                             break;
  3843.                             
  3844.                         case MID_RESET:
  3845.                             newsettings = &DefaultSettings;
  3846.                             break;
  3847.  
  3848.                         case MID_RESTORE:
  3849.                             newsettings = &RestoreSettings;
  3850.                             break;
  3851.  
  3852.                         case MID_LASTSAVED:
  3853.                             /*
  3854.                              *        If we successfully read in the defaults
  3855.                              *        file _or_ saved out a settings file, then
  3856.                              *        go ahead and read back in the last saved
  3857.                              *        file; otherwise, it's the very first
  3858.                              *        time we've ever been run by this user,
  3859.                              *        so just restore the factory settings
  3860.                              *        instead of producing an error message.
  3861.                              */
  3862.                             if (GotLastSaved) {
  3863.                                 newsettings   = 0;
  3864.                                 loadlastsaved = 1;
  3865.                             } else {
  3866.                                 newsettings   = &DefaultSettings;
  3867.                             }
  3868.                             break;
  3869.  
  3870.                         case MID_ICONS:
  3871.                             CreateIcons    = checked;
  3872.                             break;
  3873.  
  3874.                         /*
  3875.                          *        Buffer menu
  3876.                          */
  3877.                         case MID_COPYWIN:
  3878.                             /*
  3879.                              *        Copy current window to clipboard
  3880.                              */
  3881.                             DisableAllWindows();
  3882.                             if (!SaveBuffer(SAVEBUF_WINDOW, SAVEBUF_CLIPBOARD,
  3883.                                             SAVEBUF_OVERWRITE))
  3884.                                 ShowError(MSG(MSG_ERROR_COPY_WIN_TO_CLIP));
  3885.                             EnableAllWindows();
  3886.                             break;
  3887.  
  3888.                         case MID_COPYBUF:
  3889.                             /*
  3890.                              *        Copy current buffer to clipboard
  3891.                              */
  3892.                             DisableAllWindows();
  3893.                             if (!SaveBuffer(SAVEBUF_ALL, SAVEBUF_CLIPBOARD,
  3894.                                             SAVEBUF_OVERWRITE))
  3895.                                 ShowError(MSG(MSG_ERROR_COPY_ALL_TO_CLIP));
  3896.                             EnableAllWindows();
  3897.                             break;
  3898.  
  3899.                         case MID_SAVEWIN:
  3900.                             if (SelectFile(BufferFileName, BufferFileName,
  3901.                                            MainWindow, FILESEL_SAVEWINDOW))
  3902.                             {
  3903.                                 DisableAllWindows();
  3904.                                 if (!SaveBuffer(SAVEBUF_WINDOW, BufferFileName,
  3905.                                                 SAVEBUF_PROMPT))
  3906.                                 {
  3907.                                     ShowError(MSG(MSG_ERROR_SAVING_WINDOW),
  3908.                                               BufferFileName);
  3909.                                 }
  3910.                                 EnableAllWindows();
  3911.                             }
  3912.                             break;
  3913.                             
  3914.                         case MID_SAVEBUF:
  3915.                             if (SelectFile(BufferFileName, BufferFileName,
  3916.                                            MainWindow, FILESEL_SAVEBUFFER))
  3917.                             {
  3918.                                 DisableAllWindows();
  3919.                                 if (!SaveBuffer(SAVEBUF_ALL, BufferFileName,
  3920.                                                 SAVEBUF_PROMPT))
  3921.                                 {
  3922.                                     ShowError(MSG(MSG_ERROR_SAVING_BUFFER),
  3923.                                               BufferFileName);
  3924.                                 }
  3925.                                 EnableAllWindows();
  3926.                             }
  3927.                             break;
  3928.  
  3929.                         case MID_CLEARBUF:
  3930.                             ClearWindowBuffer();
  3931.                             break;
  3932.  
  3933.                     }
  3934.                     code = item->NextSelect;
  3935.                 }
  3936.                 break;
  3937.  
  3938.             case IDCMP_IDCMPUPDATE:
  3939.                 /*
  3940.                  *        Handle BOOPSI slider movement. BOOPSI arrow 
  3941.                  *        gadgets are no longer handled here but are
  3942.                  *        treated as ordinary button gadgets since
  3943.                  *        otherwise, we can't get the desired behaviour
  3944.                  *        (with a delay after the first click before it
  3945.                  *        starts repeating).
  3946.                  */
  3947.                 changedhoriz = 0;
  3948.                 changedvert  = 0;
  3949.                 updateslider = 0;
  3950.  
  3951.                 GetAttr(PGA_Top, Gadget[GID_HSCROLLER], &newval);
  3952.                 if (newval != LeftCol)
  3953.                     ScrollHorizontal(newval - LeftCol);
  3954.  
  3955.                 GetAttr(PGA_Top, Gadget[GID_VSCROLLER], &newval);
  3956.                 if (newval != (TopSeq - FirstSeq)) {
  3957.                     ShowBuffer(FirstSeq + newval, DISPLAY_QUICK);
  3958.                     CheckForDirtyMainWindow();
  3959.                 }
  3960.                 break;
  3961.  
  3962.             case IDCMP_INTUITICKS:
  3963.                 if (ScrollDirection) {
  3964.                     /*
  3965.                      *        Handle possible arrow gadget scrolling
  3966.                      */
  3967.                     if (ScrollCount >= 3) {
  3968.                         if (Gadget[ScrollDirection]->Flags & GFLG_SELECTED)
  3969.                             DoArrowScrolling(ScrollDirection, 1);
  3970.                     } else {
  3971.                         ScrollCount++;
  3972.                     }
  3973.                 }
  3974.                 if (DraggingColumn) {
  3975.                     /*
  3976.                      *        Handle horizontal scrolling to left or right;
  3977.                      *        we just treat it as a normal mouse move -- in
  3978.                      *        the general case, the displacement will be
  3979.                      *        zero, so no updating will take place.
  3980.                      */
  3981.                     MoveColumn(imsg->MouseX,
  3982.                                shift ? MOVECOL_FIXED : MOVECOL_FREE);
  3983.                 }
  3984.                 if (DraggingRow) {
  3985.                     /*
  3986.                      *        Dragging a highlight across rows, so we want to
  3987.                      *        automatically scroll if we are off the top or
  3988.                      *        bottom of the window. We also check to make sure
  3989.                      *        we're not off the top or bottom of the buffer
  3990.                      *        (no harm would come if we were, but it would
  3991.                      *        lead to unnecessary flashing of the display
  3992.                      *        highlight)
  3993.                      */
  3994.                     int scrolldir = 0;
  3995.  
  3996.                     if (imsg->MouseY < BoxInTop && TopSeq > FirstSeq)
  3997.                     {
  3998.                         scrolldir = GID_UPARROW;
  3999.                     } else if (imsg->MouseY > (BoxInTop + BoxInHeight) &&
  4000.                                BottomSeq    < EndSeq)
  4001.                     {
  4002.                         scrolldir = GID_DOWNARROW;
  4003.                     }
  4004.                     if (scrolldir) {
  4005.                         FinishDragRow();
  4006.                         DoArrowScrolling(scrolldir, 1);
  4007.                         StartDragRow(imsg->MouseY);
  4008.                     }
  4009.                 }
  4010.                 break;
  4011.  
  4012.             case IDCMP_MOUSEBUTTONS:
  4013.             {
  4014.                 static int lastrmb;
  4015.  
  4016.                 int lmb = imsg->Qualifier & IEQUALIFIER_LEFTBUTTON;
  4017.                 int rmb = imsg->Qualifier & IEQUALIFIER_RBUTTON;
  4018.  
  4019.                 if (lmb && !rmb && !lastrmb) {
  4020.                     static WORD lastx, lasty;
  4021.                     static ULONG lastsecs, lastmicros;
  4022.                     int curx = imsg->MouseX;
  4023.                     int cury = imsg->MouseY;
  4024.  
  4025.                     if (curx >= (lastx - 1) && curx <= (lastx + 1) &&
  4026.                         cury >= (lasty - 1) && cury <= (lasty + 1))
  4027.                     {
  4028.                         if (DoubleClick(lastsecs, lastmicros,
  4029.                                         imsg->Seconds, imsg->Micros)) {
  4030.                             /*
  4031.                              *        Got a double-click. Now check if it's
  4032.                              *        in the window header area. If so,
  4033.                              *        open or close the format window.
  4034.                              */
  4035.                             if (cury < BoxTop) {
  4036.                                 if (shift && FormWindow)
  4037.                                     CloseFormatWindow();
  4038.                                 else
  4039.                                     OpenFormatWindow();
  4040.                                 break;
  4041.                             }
  4042.                         }
  4043.                     }
  4044.                     lastx        = curx;
  4045.                     lasty        = cury;
  4046.                     lastsecs    = imsg->Seconds;
  4047.                     lastmicros    = imsg->Micros;
  4048.                 }
  4049.                 if (DraggingColumn) {
  4050.                     if (lmb == 0) {
  4051.                         /*
  4052.                          *        We've finished dragging our column
  4053.                          */
  4054.                         FinishDragCol();
  4055.                     } else if (rmb) {
  4056.                         /*
  4057.                          *        User clicked right button to cancel the
  4058.                          *        drag operation, so restore column width
  4059.                          *        to its original position and cancel drag.
  4060.                          */
  4061. cancel_dragging_column:
  4062.                         /* Stop highlight flash when restoring orig header */
  4063.  
  4064.  
  4065.                         if (DraggingColumn) {
  4066.                             EventFormat *nextev = DragEvent + 1;
  4067.  
  4068.                             /*
  4069.                              *        We invert the columns now and then
  4070.                              *        set SelectEvent to NULL to let
  4071.                              *        FinishDragCol() know we have already
  4072.                              *        unhighlighted them (this is because
  4073.                              *        we're hacking the window format
  4074.                              *        directly).
  4075.                              */
  4076.                             InvertColumn(SelectStartCol - LeftCol - 1,
  4077.                                          INVERT_FULLBOX);
  4078.                             InvertColumn(SelectStartCol - LeftCol +
  4079.                                          SelectEvent->width, INVERT_FULLBOX);
  4080.                             SelectEvent = NULL;
  4081.                             DragEvent->width = DragOrigColWidth;
  4082.                             if (nextev->type != EF_END)
  4083.                                 nextev->width = NextOrigColWidth;
  4084.                             BufferWidth = OrigBufWidth;
  4085.                             if (LeftCol != OrigLeftCol) {
  4086.                                 LeftCol = OrigLeftCol;
  4087.                                 InitMainMargins();
  4088.                                 UpdateMainHScroll();
  4089.                             }
  4090.                             FinishDragCol();
  4091.                         }
  4092.                         lastrmb = 1;
  4093.                         break;
  4094.                     }
  4095.                 } else if (lmb && !lastrmb) {
  4096.                     /*
  4097.                      *        Could be starting to drag a new column or row!
  4098.                      *        Check bounds to see if we are. If lastrmb was
  4099.                      *        true, then user has just released right mouse
  4100.                      *        button but hasn't yet released left mouse button,
  4101.                      *        so we ignore this attempt..
  4102.                      */
  4103.                     int x = imsg->MouseX;
  4104.                     int y = imsg->MouseY;
  4105.  
  4106.                     if (y < BoxTop && x >= BoxInLeft &&
  4107.                                       x < (BoxInLeft + BoxInWidth)) {
  4108.                         StartDragCol(x);
  4109.                     } else if (y > BoxInTop && y < (BoxInTop + BoxInHeight)) {
  4110.                         /*
  4111.                          *        Now check if correct qualifier is held down
  4112.                          *        to activate the row selection. We need to use
  4113.                          *        a slighty tricky boolean equation to handle
  4114.                          *        ROWQUAL_NONE, wher 
  4115.                          */
  4116.                         int qual = imsg->Qualifier & IE_ALL;
  4117.  
  4118.                         if (RowQual == ROWQUAL_ANY                             ||
  4119.                             (RowQual == ROWQUAL_NONE && !qual)                 ||
  4120.                             (qual && (qual & RowQualTable[RowQual]) == qual))
  4121.                         {
  4122.                             StartDragRow(y);
  4123.                         }
  4124.                     }
  4125.                 }
  4126.                 if (DraggingRow && !lmb)
  4127.                     FinishDragRow();
  4128.  
  4129.                 lastrmb = rmb;
  4130.                 break;
  4131.             }
  4132.  
  4133.             case IDCMP_MOUSEMOVE:
  4134.                 if (DraggingColumn) {
  4135.                     MoveColumn(imsg->MouseX,
  4136.                                shift ? MOVECOL_FIXED : MOVECOL_FREE);
  4137.                 }
  4138.                 if (DraggingRow)
  4139.                     MoveToRow(imsg->MouseY);
  4140.                 break;
  4141.  
  4142.             case IDCMP_GADGETUP:
  4143.             case IDCMP_GADGETDOWN:
  4144.                 gad   = (struct Gadget *)imsg->IAddress;
  4145.  
  4146. handle_maingadgets:
  4147.                 switch (gad->GadgetID) {
  4148.                     case GID_UPARROW:
  4149.                     case GID_DOWNARROW:
  4150.                     case GID_LEFTARROW:
  4151.                     case GID_RIGHTARROW:
  4152.                         if (imsg->Class == IDCMP_GADGETDOWN) {
  4153.                             /*
  4154.                              *        User clicked on a scroll button so
  4155.                              *        kick-off the scrolling.
  4156.                              */
  4157.                             ScrollDirection = gad->GadgetID;
  4158.                             DoArrowScrolling(ScrollDirection, 1);
  4159.                             MainWindow->IDCMPFlags |= IDCMP_INTUITICKS;
  4160.                             ScrollCount = 0;
  4161.                         } else {
  4162.                             /*
  4163.                              *        User let go of a scroll button so turn
  4164.                              *        off scrolling.
  4165.                              */
  4166.                             ScrollDirection = 0;
  4167.                             MainWindow->IDCMPFlags &= ~IDCMP_INTUITICKS;
  4168.                         }
  4169.                         break;
  4170.  
  4171.                     case GID_SETUP:
  4172.                         if (shift && SetWindow)
  4173.                             CloseSettingsWindow();
  4174.                         else
  4175.                             OpenSettingsWindow();
  4176.                         break;
  4177.  
  4178.                     case GID_FUNCTION:
  4179.                         if (shift && FuncWindow)
  4180.                             CloseFunctionWindow();
  4181.                         else
  4182.                             OpenFunctionWindow();
  4183.                         break;
  4184.  
  4185.                     case GID_SAVESET:
  4186.                         /*
  4187.                          *        Save settings to configuration file
  4188.                          */
  4189.                         ShowStatus(MSG(MSG_STATUS_SAVESET));
  4190.                         DisableAllWindows();
  4191.                         if (!SaveConfig(DefaultConfigName, SAVE_NOICON))
  4192.                             ShowError(MSG(MSG_ERROR_SAVING_SETTINGS),
  4193.                                       DefaultConfigName);
  4194.                         EnableAllWindows();
  4195.                         UpdateStatus();
  4196.                         break;
  4197.  
  4198.                     case GID_PAUSE:
  4199.                         if (gad->Flags & GFLG_SELECTED)
  4200.                             newmode = MONITOR_PAUSED;
  4201.                         else
  4202.                             newmode = MONITOR_NORMAL;
  4203.                         break;
  4204.  
  4205.                     case GID_DISABLE:
  4206.                         if (gad->Flags & GFLG_SELECTED)
  4207.                             newmode = MONITOR_DISABLED;
  4208.                         else
  4209.                             newmode = MONITOR_NORMAL;
  4210.                         break;
  4211.  
  4212.                     case GID_APPENDLOG:
  4213.                     case GID_STARTLOG:
  4214.                     case GID_SERIALLOG:
  4215.                         if (!OpenLog(DefaultLogMode, DefaultLogName))
  4216.                             ShowError(MSG(MSG_ERROR_STARTLOG), DefaultLogName);
  4217.                         break;
  4218.  
  4219.                     case GID_OPENLOG:
  4220. #if 0
  4221.                         /*
  4222.                          *        Quick and dirty benchmark code to scroll
  4223.                          *        the buffer 10 times to check speed
  4224.                          */
  4225.                         { int i, l;
  4226.                         
  4227.                           for (i = 0; i < 10; i++)
  4228.                             for (l = FirstSeq; l < EndSeq; l += BoxRows)
  4229.                               ShowBuffer(l, DISPLAY_QUICK);
  4230.                           break;
  4231.                         }
  4232. #endif
  4233.                         /*
  4234.                          *        Back to normal: open a new log file
  4235.                          */
  4236.                         if (SelectFile(ChosenLogName, ChosenLogName,
  4237.                                        MainWindow, FILESEL_NEWLOGNAME))
  4238.                         {
  4239.                             OpenLog(LOGMODE_PROMPT, ChosenLogName);
  4240.                         }
  4241.                         break;
  4242.                         
  4243.                     case GID_CLOSELOG:
  4244.                         CloseLog();
  4245.                         break;
  4246.  
  4247.                     case GID_HIDE:
  4248.                         dohide = 1;
  4249.                         break;
  4250.  
  4251.                     case GID_QUIT:
  4252.                         donemain = 1;
  4253.                         QuitFlag = 1;
  4254.                         break;
  4255.                 }
  4256.                 break;
  4257.  
  4258.             case IDCMP_SIZEVERIFY:
  4259.                 BeginResize();
  4260.                 break;
  4261.  
  4262.             case IDCMP_CHANGEWINDOW:
  4263.             case IDCMP_NEWSIZE:
  4264.                 if (!EndResize())
  4265.                     donemain = 1;
  4266.                 break;
  4267.             
  4268.             case IDCMP_INACTIVEWINDOW:
  4269.                 /*
  4270.                  *        If window becomes inactive, cancel any column
  4271.                  *        drag currently in operation, since it could
  4272.                  *        confuse things otherwise when the window is
  4273.                  *        later re-activated.
  4274.                  */
  4275.                 if (activegad) {
  4276.                     ShowGadget(MainWindow, Gadget[activegad], GADGET_UP);
  4277.                     activegad = 0;
  4278.                 }
  4279.                 if (DraggingRow)
  4280.                     FinishDragRow();
  4281.  
  4282.                 if (DraggingColumn)
  4283.                     goto cancel_dragging_column;
  4284.                 break;
  4285.  
  4286.             case IDCMP_RAWKEY:
  4287.             {
  4288.                 /*
  4289.                  *        Handle all keyboard shortcuts
  4290.                  */
  4291.                 int upstroke = imsg->Code & 0x80;
  4292.                 int keypress = ConvertIMsgToChar(imsg);
  4293.                 UBYTE gadid  = MainKeyboard[keypress];
  4294.  
  4295.                 if (activegad) {
  4296.                     /*
  4297.                      *        We're releasing a gadget that was pressed
  4298.                      *        earlier, so handle it now
  4299.                      */
  4300.                     int samekey = (imsg->Code & 0x7f) == activekey;
  4301.  
  4302.                     if (samekey && !upstroke)    /* Ignore repeated keys */
  4303.                         break;
  4304.  
  4305.                     ShowGadget(MainWindow, Gadget[activegad], GADGET_UP);
  4306.                     if (samekey) {
  4307.                         /*
  4308.                          *        Just released the key that was originally
  4309.                          *        pressed for this gadget, so now go and
  4310.                          *        handle the gadget action.
  4311.                          */
  4312.                         gadid     = activegad;
  4313.                         gad       = Gadget[gadid];
  4314.                         activegad = 0;
  4315.                         goto handle_maingadgets;
  4316.                     }
  4317.                     /*
  4318.                      *        If the above check didn't kick in, it means
  4319.                      *        we got a downpress of a different key, so
  4320.                      *        disable the gadget for this key and continue
  4321.                      *        to press the other key.
  4322.                      */
  4323.                     activegad = 0;
  4324.                 }
  4325.  
  4326.                 /*
  4327.                  *        Handle simple keyboard input (i.e. vanilla keys)
  4328.                  *
  4329.                  *        In addition to the gadget shortcuts in MainKeyboard[]
  4330.                  *        we also use TAB to unconditionally pause, and Space/
  4331.                  *        Return to singlestep if we're already paused.
  4332.                  *
  4333.                  *        (If we made Space always single step, then if someone
  4334.                  *        hit it by accident when the SnoopDos window was open,
  4335.                  *        it would go into pause mode which would be annoying.)
  4336.                  */
  4337.                 if (gadid) {
  4338.                     /*
  4339.                      *        Handle a gadget shortcut
  4340.                      */
  4341.                     gad = Gadget[gadid];
  4342.                     if (gadid == GID_PAUSE || gadid == GID_DISABLE) {
  4343.                         /*
  4344.                          *        PAUSE and DISABLE are toggle gadgets
  4345.                          *        which will highlight in the gadget code.
  4346.                          *        So, we handle them immediately, to give
  4347.                          *        instant feedback.
  4348.                          */
  4349.                         gad->Flags ^= GFLG_SELECTED;
  4350.                         goto handle_maingadgets;
  4351.                     } else {
  4352.                         /*
  4353.                          *        Now a slight problem ... since our logfile
  4354.                          *        gadget can cycle between multiple values,
  4355.                          *        we had better make sure that the same
  4356.                          *        shortcut will activate whichever one is
  4357.                          *        currently displayed rather than the
  4358.                          *        default hotkey (since all three opens
  4359.                          *        may share the same hotkey). If the logfile
  4360.                          *        is already open, we do nothing.
  4361.                          */
  4362.                         if (gadid == GID_OPENLOG  || gadid == GID_APPENDLOG ||
  4363.                             gadid == GID_STARTLOG || gadid == GID_SERIALLOG)
  4364.                         {
  4365.                             if (LogActive)
  4366.                                 break;
  4367.  
  4368.                             gadid = LogButtonID;
  4369.                             gad   = Gadget[gadid];
  4370.                         }
  4371.  
  4372.                         /*
  4373.                          *        All these gadget are button gadgets so we
  4374.                          *        just highlight them now, and set the
  4375.                          *        activegad flag -- on the upstroke, they'll
  4376.                          *        be released.
  4377.                          */
  4378.                         ShowGadget(MainWindow, gad, GADGET_DOWN);
  4379.                         activegad = gadid;
  4380.                         activekey = imsg->Code;
  4381.                     }
  4382.                     break;
  4383.                 }
  4384.  
  4385.                 /*
  4386.                  *        Didn't match one of the gadget hotkeys,
  4387.                  *        now check for some other special keys
  4388.                  */
  4389.                 switch (keypress) {
  4390.                     case 0x0D:        /* Return */
  4391.                         DoArrowScrolling(GID_DOWNARROW, 1);
  4392.                         break;
  4393.  
  4394.                     case ' ':        /* Space */
  4395.                         if (DraggingColumn) {
  4396.                             /*
  4397.                              *        Redraw window with current format
  4398.                              *        to date so user can see how it looks
  4399.                              */
  4400.                             InvertColumn(SelectStartCol - LeftCol - 1,
  4401.                                          INVERT_FULLBOX);
  4402.                             InvertColumn(SelectStartCol - LeftCol +
  4403.                                          SelectEvent->width, INVERT_FULLBOX);
  4404.                             BuildFormatString(BufferEFormat,
  4405.                                               BufFormat, MAX_FORM_LEN);
  4406.                             InstallNewFormat(NEW_STRING);
  4407.                             InvertColumn(SelectStartCol - LeftCol - 1,
  4408.                                          INVERT_FULLBOX);
  4409.                             InvertColumn(SelectStartCol - LeftCol +
  4410.                                          SelectEvent->width, INVERT_FULLBOX);
  4411.                         } else if (MonitorType == MONITOR_PAUSED) {
  4412.                             /*
  4413.                              *        Singlestep, but only if we were
  4414.                              *        already paused.
  4415.                              */
  4416.                             SingleStep();
  4417.                             newmode = MonitorType;
  4418.                         }
  4419.                         break;
  4420.  
  4421.                     case 0x09:        /* Tab */
  4422.                         SingleStep();
  4423.                         newmode = MonitorType;
  4424.                         break;
  4425.  
  4426.                     case 0x03:        /* CTRL-C -- quit        */
  4427.                         gadid = GID_QUIT;
  4428.                         ShowGadget(MainWindow, Gadget[gadid], GADGET_DOWN);
  4429.                         activegad = gadid;
  4430.                         activekey = imsg->Code;
  4431.                         break;
  4432.                         
  4433.                     case 0x04:        /* CTRL-D -- disable    */
  4434.                         newmode = MONITOR_DISABLED;
  4435.                         break;
  4436.                     
  4437.                     case 0x05:        /* CTRL-E -- enable        */
  4438.                         newmode = MONITOR_NORMAL;
  4439.                         break;
  4440.  
  4441.                     case 0x06:        /* CTRL-F -- to front    */
  4442.                         OpenMainWindow();
  4443.                         break;
  4444.  
  4445.                     case '\033':    /* ESC -- cancel column drag */
  4446.                         if (DraggingColumn)
  4447.                             goto cancel_dragging_column;
  4448.                         break;
  4449.                 }
  4450.  
  4451.                 /*
  4452.                  *        Finally, finally, we do a quick check to see
  4453.                  *        if the keystroke matches the format editor
  4454.                  *        menu shortcut; if it does, we open the format
  4455.                  *        editor. (If this key has been used by one of
  4456.                  *        the other gadgets, e.g. due to localisation,
  4457.                  *        then we will never get here, so not a problem.)
  4458.                  *
  4459.                  *        As with the settings and functions windows, if
  4460.                  *        the keypress is shifted, then we close the
  4461.                  *        window if it was already open.
  4462.                  */
  4463.                 if ((keypress & 0x5F) == (*MSG(MSG_WINDOWS_FORMAT) & 0x5F)) {
  4464.                     if (shift && FormWindow)
  4465.                         CloseFormatWindow();
  4466.                     else
  4467.                         OpenFormatWindow();
  4468.                     break;
  4469.                 }
  4470.  
  4471.                 /*
  4472.                  *        Handle cursor keys etc. We allow scrolling by
  4473.                  *        varying numbers of steps, depending on the
  4474.                  *        qualifier key used.
  4475.                  */
  4476.                 amount  = 1;
  4477.                 switch (imsg->Code) {
  4478.                     case CURSORUP:
  4479.                         if (shift)   amount = BoxRows;
  4480.                         if (altctrl) amount = TopSeq - FirstSeq;
  4481.                         DoArrowScrolling(GID_UPARROW, amount);
  4482.                         break;
  4483.  
  4484.                     case CURSORDOWN:
  4485.                         if (shift)   amount = BoxRows;
  4486.                         if (altctrl) amount = EndSeq - TopSeq;
  4487.                         DoArrowScrolling(GID_DOWNARROW, amount);
  4488.                         break;
  4489.  
  4490.                     case CURSORLEFT:
  4491.                         if (shift)   amount = HSCROLL_SHIFT_JUMP;
  4492.                         if (altctrl) amount = LeftCol;
  4493.                         DoArrowScrolling(GID_LEFTARROW, amount);
  4494.                         break;
  4495.  
  4496.                     case CURSORRIGHT:
  4497.                         if (shift)   amount = HSCROLL_SHIFT_JUMP;
  4498.                         if (altctrl) amount = BufferWidth - LeftCol;
  4499.                         DoArrowScrolling(GID_RIGHTARROW, amount);
  4500.                         break;
  4501.  
  4502.                     case TABKEY:
  4503.                         if (shift)
  4504.                             newmode = MONITOR_NORMAL;
  4505.                         break;
  4506.  
  4507.                     case HELPKEY:
  4508.                         ShowAGuide(MSG(MSG_LINK_MAINWIN));
  4509.                         break;
  4510.                 }
  4511.                 break;
  4512.             }
  4513.         }
  4514.         GT_ReplyIMsg(imsg);
  4515.     }
  4516.     if (newmode != MonitorType)
  4517.         SetMonitorMode(newmode);
  4518.  
  4519.     if (dohide)
  4520.         HideSnoopDos();
  4521.     else if (donemain)
  4522.         CloseMainWindow();
  4523.     else if (refreshtype != CurSettings.SimpleRefresh) {
  4524.         BoxInterGap = newspacing;
  4525.         StatusLine  = newstatus;
  4526.         GadgetsLine = newgadgets;
  4527.         CurSettings.SimpleRefresh = refreshtype;
  4528.         ReOpenMainWindow();
  4529.     } else if (newsettings) {
  4530.         InstallSettings(newsettings, SET_ALL);
  4531.     } else if (loadlastsaved) {
  4532.         /*
  4533.          *        We read the last-saved configuration from a file rather
  4534.          *        than just from a memory copy so that the user can edit
  4535.          *        the disk version by hand first if they so choose
  4536.          */
  4537.         if (!LoadConfig(ConfigFileName, MODE_INTERNAL, NULL))
  4538.             ShowError(MSG(MSG_ERROR_LOADING_SETTINGS), ConfigFileName);
  4539.     } else {
  4540.         if (newspacing != BoxInterGap)
  4541.             SetTextSpacing(newspacing);
  4542.         else if (newalign != RightAligned) {
  4543.             RightAligned = newalign;
  4544.             ShowBuffer(TopSeq, DISPLAY_ALL);
  4545.         }
  4546.         if (newgadgets != GadgetsLine || newstatus != StatusLine) {
  4547.             StatusLine  = newstatus;
  4548.             GadgetsLine = newgadgets;
  4549.  
  4550.             if (!RecalcMainWindow(MainWindow->Width, MainWindow->Height,
  4551.                                   REDRAW_GADGETS))
  4552.                 ShowError(MSG(MSG_ERROR_RESIZE));
  4553.         }
  4554.     }
  4555. }
  4556.  
  4557. /*
  4558.  *        DrawSelectedLine(row, highlight)
  4559.  *
  4560.  *        Calculates what event is currently positioned at the specified row
  4561.  *        in the main window and redraws that row either highlighted or
  4562.  *        unhighlighted. If the event is now complete, its state is updated
  4563.  *        accordingly. If no event can be found at that line, then the nearest
  4564.  *        row is highlighted instead.
  4565.  *
  4566.  *        If highlight is true, the row is highlighted, else it is unhighlighted.
  4567.  */
  4568. void DrawSelectedLine(int row, int highlight)
  4569. {
  4570.     struct RastPort *rport = MainWindow->RPort;
  4571.     Event *ev;
  4572.     Event *firstevent;
  4573.     int   firstseq;
  4574.     int   fillpen = 0;
  4575.     int   textpen = 1;
  4576.     int   i;
  4577.  
  4578.     if (IsListEmpty(&EventList) || BufferEFormat[0].type == EF_END)
  4579.         return;
  4580.  
  4581.     /*
  4582.      *        First, find the event
  4583.      */
  4584.     LOCK_LAYERS;
  4585.     ObtainSemaphore(&BufSem);
  4586.     firstevent = HeadNode(&EventList);
  4587.     firstseq   = firstevent->seqnum;
  4588.     ev         = TopEvent;
  4589.     if (TopSeq < firstseq)
  4590.         ev = firstevent;
  4591.     
  4592.     for (i = 0; i < row && ev != TailNode(&EventList); i++, ev = NextNode(ev))
  4593.         ;
  4594.     if (ev->status == ES_READY)
  4595.         ev->status = ES_ACCEPTED;
  4596.  
  4597.     FormatEvent(BufferEFormat, ev, BufferLine, LeftCol, LeftCol + BoxCols - 1);
  4598.     /*
  4599.      *        Now setup rastport according to whether or not we want it
  4600.      *        highlighted
  4601.      */
  4602.     if (highlight) {
  4603.         fillpen = ScreenDI->dri_Pens[FILLPEN];
  4604.         textpen = ScreenDI->dri_Pens[FILLTEXTPEN];
  4605.     }
  4606.     SetAPen(rport, textpen);
  4607.     SetBPen(rport, fillpen);
  4608.     SetDrMd(rport, JAM2);
  4609.     Move(rport, BoxInLeft, BoxBaseline + i * BoxSpacing);
  4610.     Text(rport, BufferLine, BoxCols);
  4611.     ReleaseSemaphore(&BufSem);
  4612.     UNLOCK_LAYERS;
  4613. }
  4614.  
  4615. /*
  4616.  *        OutputBufLine(event, row)
  4617.  *
  4618.  *        Outputs the specified event to the buffer display at the given
  4619.  *        row, using the current format, left and right col indicators.
  4620.  *        See also DrawSelectedLine() above.
  4621.  */
  4622. void OutputBufLine(Event *event, int row)
  4623. {
  4624.     struct RastPort *rport = MainWindow->RPort;
  4625.  
  4626.     if (event->status == ES_READY)
  4627.         event->status = ES_ACCEPTED;
  4628.  
  4629.     FormatEvent(BufferEFormat, event, BufferLine, LeftCol, RightCol);
  4630.     Move(rport, BoxInLeft, BoxBaseline + row*BoxSpacing);
  4631.     Text(rport, BufferLine, RightCol - LeftCol + 1);
  4632. }
  4633.  
  4634. /*
  4635.  *        ClearWindowBuffer()
  4636.  *
  4637.  *        Clears the buffer area on the screen, and empties the internal
  4638.  *        buffer as well (called by the Clear Buffer menu option).
  4639.  */
  4640. void ClearWindowBuffer(void)
  4641. {
  4642.     ClearBuffer();
  4643.     if (MainWindow) {
  4644.         struct RastPort *rport = MainWindow->RPort;
  4645.  
  4646.         /*
  4647.          *        Erase strip to right of our rendered text
  4648.          */
  4649.         SetAPen(rport, 0);
  4650.         SetDrMd(rport, JAM1);
  4651.         RectFill(rport, BoxInLeft, BoxInTop,
  4652.                         BoxInLeft+BoxInWidth-1, BoxInTop+BoxInHeight-1);
  4653.  
  4654.         UpdateMainVScroll();
  4655.     }
  4656.     ClearMainRHS = 0;
  4657. }
  4658.  
  4659. /*
  4660.  *        ShowBuffer(seqnum, displaytype)
  4661.  *
  4662.  *        Adjusts the buffer display so that it begins at event numbered
  4663.  *        seqnum. If more than half of the needed events are currently
  4664.  *        on display, the buffer is scrolled to put them in the correct
  4665.  *        position. Any lines not currently on display are redrawn.
  4666.  *
  4667.  *        Updates TopSeq and BottomSeq accordingly.
  4668.  *
  4669.  *        Also does validity checking to ensure that if our current set of
  4670.  *        events has scrolled off the top of the buffer, we will still be
  4671.  *        displayed.
  4672.  *
  4673.  *        Displaytype is DISPLAY_ALL or DISPLAY_QUICK, to force either
  4674.  *        the entire buffer to be redrawn dumbly at the new position, or
  4675.  *        to optimise it by scrolling, only drawing changed items, etc.
  4676.  *
  4677.  *        If displaytype is DISPLAY_NONE, then all the Seq variables are
  4678.  *        recalculated to take account of events scrolling off the top
  4679.  *        etc. but no new output is displayed. This allows us to update
  4680.  *        the scrollbar without interrupting the current screen display
  4681.  *        while the user is reading it.
  4682.  *
  4683.  *        Note: it is save to call ShowBuffer with DISPLAY_NONE when the
  4684.  *        main window is closed, but for all the others, it should be open.
  4685.  */
  4686. void ShowBuffer(LONG seqnum, int displaytype)
  4687. {
  4688.     struct RastPort *rport;
  4689.     LONG  oldtopseq = TopSeq;        /* Saved old top position                */
  4690.     LONG  firstseq;                    /* First sequence number on list        */
  4691.     Event *firstevent;                /* First event on list                    */
  4692.     Event *topev;                    /* New top event displayed on screen    */
  4693.     int   row = 0;                    /* Current output row in buffer            */
  4694.     int   scrollthresh;                /* If moving less than this, use scroll    */
  4695.  
  4696.     /*
  4697.      *        If there is a lot of activity, then sometimes we can get
  4698.      *        output messages in between an IDCMP_VERIFY and an IDCMP_NEWSIZE
  4699.      *        which can lead to screen artefacts appearing in the border.
  4700.      *        One way to avoid these is to always refresh the borders when
  4701.      *        we get an IDCMP_NEWSIZE, but a neater way is to suspend output
  4702.      *        while we're between the two types of message -- this is less
  4703.      *        visually jarring on the user.
  4704.      */
  4705.     if (AwaitingResize != RESIZE_DONE)
  4706.         displaytype = DISPLAY_NONE;
  4707.  
  4708.     /*
  4709.      *        Important that we lock our layers -- see HandleNewEvents()
  4710.      *        for a more detailed explanation of why.
  4711.      */
  4712.     DB("ShowBuffer-Semaphore\n");
  4713.     LOCK_LAYERS;
  4714.     ObtainSemaphore(&BufSem);
  4715.  
  4716.     if (IsListEmpty(&EventList) ||
  4717.                     ((Event *)HeadNode(&EventList))->status == ES_CREATING)
  4718.         goto done_show_buffer;
  4719.  
  4720.     firstevent = HeadNode(&EventList);
  4721.     firstseq   = firstevent->seqnum;
  4722.  
  4723.     if (TopSeq < firstseq) {
  4724.         TopEvent    = firstevent;
  4725.         TopSeq      = firstseq;
  4726.         BottomSeq   = firstseq;
  4727.         /*
  4728.          *        The events currently on display in the window must have
  4729.          *        scrolled off the top of the buffer, so we can't assume
  4730.          *        any of them still exist. So, force a full redraw instead.
  4731.          */
  4732.         if (displaytype == DISPLAY_QUICK)
  4733.             displaytype = DISPLAY_ALL;
  4734.     }
  4735.     if (EndSeq < firstseq) {
  4736.         EndSeq      = firstseq;
  4737.         EndEvent    = firstevent;
  4738.     }
  4739.     if (seqnum < firstseq)    seqnum = firstseq;
  4740.     if (seqnum > EndSeq)    seqnum = EndSeq;
  4741.     
  4742.     /*
  4743.      *        Work out if we need to scan forwards or backwards from the
  4744.      *        current event at the top of the screen
  4745.      */
  4746.     if (seqnum <= TopSeq) {
  4747.         /*
  4748.          *        Scanning backwards from current top of screen. Now see if
  4749.          *        it would be faster to scan forward from the start of the
  4750.          *        list.
  4751.          */
  4752.         if ((TopSeq - seqnum) < (seqnum - firstseq)) {
  4753.             for (topev = TopEvent;
  4754.                      topev != HeadNode(&EventList) && topev->seqnum > seqnum;
  4755.                      topev = PrevNode(topev))
  4756.                 ;
  4757.         } else {
  4758.             for (topev = firstevent;
  4759.                     topev != TailNode(&EventList) && topev->seqnum < seqnum;
  4760.                      topev = NextNode(topev))
  4761.                 ;
  4762.         }
  4763.     } else { /* seqnum > TopSeq */
  4764.         /*
  4765.          *        Scanning forwards from the current top of the screen. Now
  4766.          *        see if it would be faster to scan backwards from the end of
  4767.          *        the event list.
  4768.          */
  4769.         if ((EndSeq - seqnum) < (seqnum - TopSeq)) {
  4770.             for (topev = EndEvent; topev != TopEvent && topev->seqnum > seqnum;
  4771.                                    topev = PrevNode(topev))
  4772.                 ;
  4773.         } else {
  4774.             for (topev = TopEvent; topev != EndEvent && topev->seqnum < seqnum;
  4775.                                    topev = NextNode(topev))
  4776.                 ;
  4777.         }
  4778.     }
  4779.     
  4780.     /*
  4781.      *        Now topev is the new top event to display in the window.
  4782.      *        But wait! What if this topev is close to the bottom of our
  4783.      *        list? We want to always show a full window of text if
  4784.      *        possible. So, adjust it to ensure as many events as possible
  4785.      *        can fit in the screen.
  4786.      */
  4787.     while (topev != firstevent && seqnum > (EndSeq - BoxRows + 1)) {
  4788.         topev = PrevNode(topev);
  4789.         seqnum--;
  4790.     }
  4791.  
  4792.     /*
  4793.      *        Okay! Now we finally have topev pointing to our new top of
  4794.      *        screen node. Now update our display accordingly. We also
  4795.      *        take this opportunity to refresh our scroll bar, since it's
  4796.      *        relative position may well now be different.
  4797.      */
  4798.     FirstSeq  = firstseq;
  4799.     TopEvent  = topev;
  4800.     TopSeq    = seqnum;
  4801.  
  4802.     if ((displaytype & DISPLAY_NONE) || !MainWindow)
  4803.         goto done_show_buffer;
  4804.  
  4805.     SetupBufferRastPort();
  4806.     rport = MainWindow->RPort;
  4807.  
  4808.     /*
  4809.      *        We record the top line that was last drawn into the buffer. This
  4810.      *        is useful when ShowBuffer(..., DISPLAY_NONE) is called since it
  4811.      *        let's us detect when the info inside the window is no longer valid
  4812.      *        due to having scrolled off the top of the buffer.
  4813.      */
  4814.     if (seqnum != oldtopseq)
  4815.         LastDrawnTopSeq = TopSeq;
  4816.  
  4817.     if (displaytype & DISPLAY_ALL)
  4818.         goto redrawall;
  4819.     
  4820.     /*
  4821.      *        Setup so we only render to bitplane 0 -- this greatly
  4822.      *        speeds up text output and rendering speed, especially
  4823.      *        on deep Workbenches.
  4824.      */
  4825.     if (GfxBase->LibNode.lib_Version >= 39)
  4826.         SetWriteMask(rport, 1);        /* Speed up scrolling and rendering! */
  4827.     else
  4828.         rport->Mask = 1;
  4829.  
  4830.     /* 
  4831.      *        Now setup our scroll threshold. If running on V37 (where
  4832.      *        when we scroll, we get backfilled with colour 0 before
  4833.      *        we can redraw), then our threshold is 1/2 the displayed
  4834.      *        number of rows. If running on V39 or above, we increase
  4835.      *        the threshold to 3/4 of the displayed rows.
  4836.      */
  4837.     scrollthresh = BoxRows / 2;
  4838.     if (GfxBase->LibNode.lib_Version >= 39)
  4839.         scrollthresh = (BoxRows * 3) / 4;
  4840.  
  4841.     if (seqnum == oldtopseq) {
  4842.         /*
  4843.          *        We're already at the correct position in the buffer, so
  4844.          *        just scan looking for new or updated entries (i.e. any
  4845.          *        which are not yet ES_ACCEPTED)
  4846.          */
  4847.         while (row < BoxRows && seqnum <= EndSeq) {
  4848.             if (topev->status != ES_ACCEPTED)
  4849.                 OutputBufLine(topev, row);
  4850.             topev = NextNode(topev);
  4851.             seqnum++;
  4852.             row++;
  4853.         }
  4854.     } else if (seqnum > oldtopseq && (seqnum - oldtopseq) <= scrollthresh) {
  4855.         /*
  4856.          *        Scrolling forward, so move buffer up.
  4857.          *
  4858.          *        If running on V39 or above, we disable background fills
  4859.          *        for our layer while we ScrollRaster() since we're going
  4860.          *        to be redrawing the area anyway -- it speeds things up,
  4861.          *        and is also less visually obtrusive.
  4862.          */
  4863.         int dy = seqnum - oldtopseq;
  4864.         int scrollwidth = (RightCol - LeftCol + 1) * BoxCharWidth;
  4865.  
  4866.         DB("ScrollRaster start\n");
  4867.         if (GfxBase->LibNode.lib_Version >= 39) {
  4868.             struct Hook *oldhook;
  4869.  
  4870.             oldhook = InstallLayerHook(MainWindow->WLayer, LAYERS_NOBACKFILL);
  4871.             ScrollRasterBF(rport, 0, dy * BoxSpacing,
  4872.                            BoxInLeft, BoxInTop,
  4873.                            BoxInLeft + scrollwidth-1, BoxInTop + BoxInHeight-1);
  4874.             InstallLayerHook(MainWindow->WLayer, oldhook);
  4875.         } else {
  4876.             ScrollRaster(rport, 0, dy * BoxSpacing,
  4877.                          BoxInLeft, BoxInTop,
  4878.                          BoxInLeft + scrollwidth-1, BoxInTop + BoxInHeight-1);
  4879.         }
  4880.         DB("ScrollRaster end\n");
  4881.                      
  4882.         while (seqnum <= EndSeq && row < (BoxRows - dy)) {
  4883.             /*
  4884.              *        Even though these lines are already present,
  4885.              *        we still have to re-output any lines that are
  4886.              *        not ES_ACCEPTED since they may have been
  4887.              *        updated since the last time they were displayed.
  4888.              */
  4889.             if (topev->status != ES_ACCEPTED)
  4890.                 OutputBufLine(topev, row);
  4891.             topev = NextNode(topev);
  4892.             seqnum++;
  4893.             row++;
  4894.         }
  4895.         while (seqnum <= EndSeq && row < BoxRows) {
  4896.             OutputBufLine(topev, row);
  4897.             topev = NextNode(topev);
  4898.             seqnum++;
  4899.             row++;
  4900.         }
  4901.     } else if (oldtopseq > seqnum && (oldtopseq - seqnum) <= scrollthresh) {
  4902.         /*
  4903.          *        Scrolling backward, so move buffer down.
  4904.          */
  4905.         int dy = oldtopseq - seqnum;
  4906.         int scrollwidth = (RightCol - LeftCol + 1) * BoxCharWidth;
  4907.  
  4908.         DB("ScrollRaster start\n");
  4909.         if (GfxBase->LibNode.lib_Version >= 39) {
  4910.             struct Hook *oldhook;
  4911.  
  4912.             oldhook = InstallLayerHook(MainWindow->WLayer, LAYERS_NOBACKFILL);
  4913.             ScrollRasterBF(rport, 0, -dy * BoxSpacing,
  4914.                            BoxInLeft, BoxInTop,
  4915.                            BoxInLeft + scrollwidth-1, BoxInTop+BoxInHeight-1);
  4916.             InstallLayerHook(MainWindow->WLayer, oldhook);
  4917.         } else {
  4918.             ScrollRaster(rport, 0, -dy * BoxSpacing,
  4919.                          BoxInLeft, BoxInTop,
  4920.                          BoxInLeft + scrollwidth-1, BoxInTop + BoxInHeight-1);
  4921.         }
  4922.         DB("ScrollRaster end\n");
  4923.         /*
  4924.          *        Update the new (blank) areas first
  4925.          */
  4926.         while (seqnum <= oldtopseq) {
  4927.             OutputBufLine(topev, row);
  4928.             topev = NextNode(topev);
  4929.             seqnum++;
  4930.             row++;
  4931.         }
  4932.         /*
  4933.          *        Now can the remaining lines in the buffer (which are
  4934.          *        already on the screen) and re-output any which are
  4935.          *        not ES_ACCEPTED, since they may have changed since the
  4936.          *        last update.
  4937.          */
  4938.         while (row < BoxRows && seqnum <= EndSeq) {
  4939.             if (topev->status != ES_ACCEPTED)
  4940.                 OutputBufLine(topev, row);
  4941.             topev = NextNode(topev);
  4942.             seqnum++;
  4943.             row++;
  4944.         }
  4945.     } else {
  4946.         /*
  4947.          *        The move is too big to handle by scrolling, so just
  4948.          *        redraw the entire buffer instead.
  4949.          */
  4950. redrawall:
  4951.         while (row < BoxRows && seqnum <= EndSeq) {
  4952.             OutputBufLine(topev, row);
  4953.             topev = NextNode(topev);
  4954.             seqnum++;
  4955.             row++;
  4956.         }
  4957.     }
  4958.  
  4959.     if ((displaytype & DISPLAY_ALL) == 0) {
  4960.         /*
  4961.          *        Undo the bitplane optimisation we did earlier, but only
  4962.          *        if we're doing an optimised refresh.
  4963.          */
  4964.         if (GfxBase->LibNode.lib_Version >= 39)
  4965.             SetWriteMask(rport, 0xFF);
  4966.         else
  4967.             rport->Mask = 0xff;
  4968.     }
  4969.     BottomSeq = seqnum - 1;    /* Bottom most sequence number in window */
  4970.  
  4971. done_show_buffer:
  4972.     DB("ShowBuffer-Semaphore-done\n");
  4973.     ReleaseSemaphore(&BufSem);
  4974.     UNLOCK_LAYERS;
  4975. }
  4976.  
  4977. /*
  4978.  *        HandleNewEvents()
  4979.  *
  4980.  *        Called whenever we are signalled that a new event has arrived
  4981.  *        in the input queue.
  4982.  *
  4983.  *        We simply scan the queue from our last known position to our
  4984.  *        current position and, if necessary, update our output window
  4985.  *        to reflect any new events. We also output any new _complete_
  4986.  *        events to the logfile.
  4987.  */
  4988. void HandleNewEvents(void)
  4989. {
  4990.     Event *firstevent;
  4991.     Event *newev;
  4992.     Event *ev;
  4993.     int   curendcompleteseq = EndCompleteSeq;
  4994.     int   curendseq         = EndSeq;
  4995.     LONG  newseq;
  4996.     int   refresh = 0;
  4997.  
  4998.     DB("HandleNewEvent-Semaphore\n");
  4999.  
  5000.     if (IsListEmpty(&EventList) || AwaitingResize != RESIZE_DONE
  5001.                                 || DraggingColumn || DraggingRow)
  5002.         return;
  5003.     
  5004.     /*
  5005.      *        It is vital that for the duration of the screen update,
  5006.      *        we retain complete access to our output rastport. Otherwise,
  5007.      *        we can run into a deadlock where a higher priority task
  5008.      *        (like input.device) locks the screen layer's while we
  5009.      *        are in the middle of our refresh, and then calls one of
  5010.      *        our patched functions (like OpenFont) -- instant hang
  5011.      *        (because we own the buffer semaphore and the patched
  5012.      *        function will sit waiting for us to free it).
  5013.      *
  5014.      *        By locking our layers, we ensure that nobody else can
  5015.      *        ever get in once we start our refresh. The downside is
  5016.      *        that this locks out menu rendering etc, but since we'll
  5017.      *        be quick at refreshing, that's okay.
  5018.      *
  5019.      *        Note that we lock the layers _before_ getting the buffer
  5020.      *        semaphore -- if we do it afterwards, there is a window of
  5021.      *        opportunity where we task switch to a higher priority task
  5022.      *        between the two calls which then proceeds to lock layers
  5023.      *        and try and lock the buffer semaphore -- deadlock again!
  5024.      *
  5025.      *        News Flash! We don't lock layers any more, since it lead
  5026.      *        to jerky performance when dragging the scrollbar etc.
  5027.      *        Instead, our patch code checks to see if our layers are
  5028.      *        locked by the calling task and if they are, bypasses
  5029.      *        outputting the event completely. Since the only things
  5030.      *        that ever call our patches when layers are locked are
  5031.      *        low-level Intuition functions that are re-rendering
  5032.      *        gadgets etc (and calling OpenFont()) this is actually
  5033.      *        a win-win since we're typically not interested in those
  5034.      *        events anyway.
  5035.      */        
  5036.     if (MainWindow) {
  5037.         LOCK_LAYERS;        /* This is now a no-op */
  5038.     }
  5039.     ObtainSemaphore(&BufSem);
  5040.  
  5041.     if (EndSeq == BottomSeq)             /* If at end of buf, show new o/p    */
  5042.         refresh = 1;
  5043.  
  5044.     firstevent = HeadNode(&EventList);
  5045.     if (firstevent->seqnum > EndCompleteSeq)
  5046.         newev = firstevent;
  5047.     else if (EndCompleteEvent == TailNode(&EventList)) {
  5048.         newev = EndCompleteEvent;
  5049.     } else {
  5050.         newev = NextNode(EndCompleteEvent);
  5051.     }
  5052.     newseq = newev->seqnum;
  5053.     
  5054.     /*
  5055.      *        First we scan all the new incoming entries to figure out
  5056.      *        our new EndEvent/EndSeq (for ShowBuffer)
  5057.      */
  5058.     for (ev = newev; NextNode(ev) != NULL && ev->status != ES_CREATING;
  5059.                      ev = NextNode(ev))
  5060.         ;
  5061.     EndEvent = PrevNode(ev);
  5062.     EndSeq   = EndEvent->seqnum;
  5063.  
  5064.     if (MainWindow) {
  5065.         if (refresh) {
  5066.             /*
  5067.              *        We were already displaying the end of the buffer and we
  5068.              *        got new info in, so move the display to the new end of
  5069.              *        buffer.
  5070.              */
  5071.             ShowBuffer(EndSeq, DISPLAY_QUICK);
  5072.         } else if (BottomSeq > EndCompleteSeq) {
  5073.             /*
  5074.              *        We weren't at the very end of the buffer, but there was an
  5075.              *        unfinished event displayed somewhere on the screen, so
  5076.              *        rescan the currently displayed bufferlines in case it
  5077.              *        needs to be updated.
  5078.              */
  5079.             ShowBuffer(TopSeq, DISPLAY_QUICK);        
  5080.         } else {
  5081.             /*
  5082.              *        We're too far up in the buffer to show any of the
  5083.              *        new data, but update our internal variables anyway
  5084.              *        so we can keep the scroll bar up to date.
  5085.              */
  5086.             ShowBuffer(TopSeq, DISPLAY_NONE);
  5087.         }
  5088.     } else {
  5089.         /*
  5090.          *        Simply update TopSeq accordingly so it will be correct the
  5091.          *        next time we open the window
  5092.          */
  5093.         TopSeq     = EndSeq;
  5094.         TopEvent = EndEvent;
  5095.     }
  5096.  
  5097.     /*
  5098.      *        Now mark all new events which are finished as complete, so that
  5099.      *        we won't rescan them
  5100.      */
  5101.     for (ev = newev; NextNode(ev) != NULL && ev->status >= ES_READY;
  5102.                      ev = NextNode(ev)) {
  5103.         EndCompleteEvent = ev;
  5104.         EndCompleteSeq   = ev->seqnum;
  5105.     }
  5106.     DB("HandleNewEvent-Semaphore-done\n");
  5107.     ReleaseSemaphore(&BufSem);
  5108.  
  5109.     if (MainWindow) {
  5110.         CheckForDirtyMainWindow();
  5111.         UNLOCK_LAYERS;
  5112.         UpdateMainVScroll();
  5113.     }
  5114.  
  5115.     if (LogActive && !Disabled) {
  5116.         /*
  5117.          *        Now output the new events to our logfile. We have to be
  5118.          *        very careful doing this since we must not keep the buffer
  5119.          *        semaphore locked while doing file output (it could lead
  5120.          *        to deadlocks if the file output causes a requester to
  5121.          *        appear on the screen, for example). Thus, we must lock
  5122.          *        and unlock the semaphore as we scan each event. Sigh.
  5123.          */
  5124.         LONG latestready;        /* seqnum of highest event that's ES_READY */
  5125.  
  5126.         /*
  5127.          *        Output the new events to the logfile. We only output
  5128.          *        events that are complete, or events that are incomplete
  5129.          *        but which have a complete event after them. Any complete
  5130.          *        events that have been output earlier are not output
  5131.          *        again, unless they were only partially output the first
  5132.          *        time.
  5133.          */
  5134.         ObtainSemaphore(&BufSem);
  5135.         ev = HeadNode(&EventList);
  5136.         if (ev->seqnum > newseq) {
  5137.             newev  = ev;
  5138.             newseq = ev->seqnum;
  5139.         } else if (newseq == curendcompleteseq) {
  5140.             /*
  5141.              *        We couldn't advance past the endcompleteseq earlier on
  5142.              *        because there was nothing beyond it, so step past now.
  5143.              */
  5144.             newev = NextNode(newev);
  5145.             newseq++;
  5146.         }
  5147.         for (ev = TailNode(&EventList);
  5148.             ev->seqnum > newseq && ev->status < ES_READY; ev = PrevNode(ev))
  5149.             ;
  5150.         latestready = ev->seqnum;
  5151.         if (ev->status < ES_READY)
  5152.             latestready--;        /* Just in case ev->seqnum == newseq */
  5153.  
  5154.         for (ev = newev; NextNode(ev) != NULL && ev->seqnum <= latestready;
  5155.                          ev = NextNode(ev)) {
  5156.             int firstchar = 0;
  5157.  
  5158.             /*
  5159.              *        For each event that is either complete, or partially
  5160.              *        complete but followed by a complete event, output it
  5161.              *        to the logfile (unless already output).
  5162.              */
  5163.             if (ev->status >= ES_READY) {
  5164.                 if ((ev->flags & EFLG_LOGDONE) == 0) {
  5165.                     if (ev->flags & EFLG_LOGPARTIAL)
  5166.                         firstchar = '\\';
  5167.                     else
  5168.                         firstchar = ' ';
  5169.                     ev->flags |= EFLG_LOGDONE;
  5170.                 }
  5171.             } else if ((ev->flags & EFLG_LOGPARTIAL) == 0) {
  5172.                 /*
  5173.                  *        Output partial event
  5174.                  */
  5175.                 ev->flags |= EFLG_LOGPARTIAL;
  5176.                 firstchar = '/';
  5177.             }
  5178.  
  5179.             if (firstchar) {
  5180.                 /*
  5181.                  *        Output the line
  5182.                  */
  5183.                 FormatEvent(LogEFormat, ev, BufferLine+1, 0, LogWidth - 1);
  5184.                 BufferLine[0]              = firstchar;
  5185.                 BufferLine[LogWidth+1] = '\n';
  5186.                 BufferLine[LogWidth+2] = 0;
  5187.                 ReleaseSemaphore(&BufSem);
  5188.                 WriteLog(BufferLine);
  5189.                 ObtainSemaphore(&BufSem);
  5190.                 /*
  5191.                  *        While we had the semaphore released, it's possible
  5192.                  *        that the event we're on may have scrolled off the
  5193.                  *        buffer. If it has, then give up right now (we'll
  5194.                  *        catch the new ones the next time around).
  5195.                  */
  5196.                 if (((Event *)HeadNode(&EventList))->seqnum > latestready)
  5197.                     break;
  5198.             }
  5199.         }
  5200.         ReleaseSemaphore(&BufSem);
  5201.         if (Paused)
  5202.             WriteLog(NULL);    /* Flush log if we're single-stepping */
  5203.     }
  5204.     if (AutoOpen && !MainWindow && curendseq < EndSeq)
  5205.         ShowSnoopDos();
  5206. }
  5207.  
  5208. /*
  5209.  *        CleanupMainWindow()
  5210.  *
  5211.  *        Frees any resources associated with this module
  5212.  */
  5213. void CleanupMainWindow(void)
  5214. {
  5215.     CloseMainWindow();
  5216.     if (MainGadList)    FreeGadgets(MainGadList),    MainGadList        = NULL;
  5217.     if (BufferFont)        CloseFont(BufferFont),        BufferFont        = NULL;
  5218.     if (LogActive)        CloseLog();
  5219. }
  5220.